Skip to content

Commit d00799a

Browse files
authored
Merge pull request #2436 from NTaylorMullen/nimullen/2431
Track virtual documents.
2 parents 82be5e1 + 51da252 commit d00799a

File tree

6 files changed

+173
-3
lines changed

6 files changed

+173
-3
lines changed

src/features/diagnosticsProvider.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ class DiagnosticsProvider extends AbstractSupport {
140140
// Go ahead and check for diagnostics in the currently visible editors.
141141
for (let editor of vscode.window.visibleTextEditors) {
142142
let document = editor.document;
143-
if (document.languageId === 'csharp' && document.uri.scheme === 'file') {
143+
if (document.languageId === 'csharp') {
144144
this._validateDocument(document);
145145
}
146146
}
@@ -172,7 +172,7 @@ class DiagnosticsProvider extends AbstractSupport {
172172
}
173173

174174
private _onDocumentAddOrChange(document: vscode.TextDocument): void {
175-
if (document.languageId === 'csharp' && document.uri.scheme === 'file') {
175+
if (document.languageId === 'csharp') {
176176
this._validateDocument(document);
177177
this._validateProject();
178178
}
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
import {workspace, TextDocument, Uri} from 'vscode';
7+
import {OmniSharpServer} from '../omnisharp/server';
8+
import * as serverUtils from '../omnisharp/utils';
9+
import { FileChangeType } from '../omnisharp/protocol';
10+
import { IDisposable } from '../Disposable';
11+
import CompositeDisposable from '../CompositeDisposable';
12+
import { EventStream } from '../EventStream';
13+
import { DocumentSynchronizationFailure } from '../omnisharp/loggingEvents';
14+
15+
function trackCurrentVirtualDocuments(server: OmniSharpServer, eventStream: EventStream) {
16+
let registration = server.onProjectAdded(async () => {
17+
registration.dispose();
18+
19+
for (let i = 0; i < workspace.textDocuments.length; i++) {
20+
let document = workspace.textDocuments[i];
21+
22+
if (!shouldIgnoreDocument(document, server)) {
23+
await openVirtualDocument(document, server, eventStream);
24+
}
25+
}
26+
});
27+
}
28+
29+
function trackFutureVirtualDocuments(server: OmniSharpServer, eventStream: EventStream): IDisposable {
30+
let onTextDocumentOpen = workspace.onDidOpenTextDocument(async document => {
31+
if (shouldIgnoreDocument(document, server)) {
32+
return;
33+
}
34+
35+
await openVirtualDocument(document, server, eventStream);
36+
});
37+
38+
let onTextDocumentClose = workspace.onDidCloseTextDocument(async document => {
39+
if (shouldIgnoreDocument(document, server)) {
40+
return;
41+
}
42+
43+
await closeVirtualDocument(document, server, eventStream);
44+
});
45+
46+
// We already track text document changes for virtual documents in our change forwarder.
47+
return new CompositeDisposable(
48+
onTextDocumentOpen,
49+
onTextDocumentClose);
50+
}
51+
52+
function shouldIgnoreDocument(document: TextDocument, server: OmniSharpServer): boolean {
53+
if (document.uri.scheme === 'file' || document.languageId !== 'csharp') {
54+
// We're only interested in non-physical CSharp documents.
55+
return true;
56+
}
57+
58+
if (!server.isRunning()) {
59+
return true;
60+
}
61+
62+
return false;
63+
}
64+
65+
async function openVirtualDocument(document: TextDocument, server: OmniSharpServer, eventStream: EventStream) {
66+
let req = { FileName: document.uri.path, changeType: FileChangeType.Create };
67+
try {
68+
await serverUtils.filesChanged(server, [req]);
69+
await serverUtils.updateBuffer(server, { Buffer: document.getText(), FileName: document.fileName });
70+
}
71+
catch (error) {
72+
logSynchronizationFailure(document.uri, error, server, eventStream);
73+
}
74+
}
75+
76+
async function closeVirtualDocument(document: TextDocument, server: OmniSharpServer, eventStream: EventStream) {
77+
let req = { FileName: document.uri.path, changeType: FileChangeType.Delete };
78+
try {
79+
await serverUtils.filesChanged(server, [req]);
80+
}
81+
catch (error) {
82+
logSynchronizationFailure(document.uri, error, server, eventStream);
83+
}
84+
}
85+
86+
function logSynchronizationFailure(uri: Uri, error: any, server: OmniSharpServer, eventStream: EventStream) {
87+
if (server.isRunning()) {
88+
eventStream.post(new DocumentSynchronizationFailure(uri.path, error));
89+
}
90+
}
91+
92+
export default function trackVirtualDocuments(server: OmniSharpServer, eventStream: EventStream): IDisposable {
93+
trackCurrentVirtualDocuments(server, eventStream);
94+
let disposable = trackFutureVirtualDocuments(server, eventStream);
95+
96+
return disposable;
97+
}

src/observers/CsharpLoggerObserver.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ export class CsharpLoggerObserver extends BaseLoggerObserver {
5252
case Event.DownloadSizeObtained.name:
5353
this.handleDownloadSizeObtained(<Event.DownloadSizeObtained>event);
5454
break;
55+
case Event.DocumentSynchronizationFailure.name:
56+
this.handleDocumentSynchronizationFailure(<Event.DocumentSynchronizationFailure>event);
57+
break;
5558
case Event.LatestBuildDownloadStart.name:
5659
this.logger.appendLine("Getting latest OmniSharp version information");
5760
break;
@@ -112,4 +115,8 @@ export class CsharpLoggerObserver extends BaseLoggerObserver {
112115
this.logger.appendLine(`Installing package '${event.packageDescription}'`);
113116
this.logger.appendLine();
114117
}
118+
119+
private handleDocumentSynchronizationFailure(event: Event.DocumentSynchronizationFailure) {
120+
this.logger.appendLine(`Failed to synchronize document '${event.documentPath}': ${event.errorMessage}`);
121+
}
115122
}

src/omnisharp/extension.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,14 @@ import { NetworkSettingsProvider } from '../NetworkSettings';
3434
import CompositeDisposable from '../CompositeDisposable';
3535
import Disposable from '../Disposable';
3636
import OptionProvider from '../observers/OptionProvider';
37+
import trackVirtualDocuments from '../features/virtualDocumentTracker';
3738
import { StructureProvider } from '../features/structureProvider';
3839

3940
export let omnisharp: OmniSharpServer;
4041

4142
export async function activate(context: vscode.ExtensionContext, packageJSON: any, platformInfo: PlatformInformation, provider: NetworkSettingsProvider, eventStream: EventStream, optionProvider: OptionProvider, extensionPath: string) {
4243
const documentSelector: vscode.DocumentSelector = {
4344
language: 'csharp',
44-
scheme: 'file' // only files from disk
4545
};
4646

4747
const options = optionProvider.GetLatestOptions();
@@ -81,6 +81,7 @@ export async function activate(context: vscode.ExtensionContext, packageJSON: an
8181
localDisposables.add(vscode.languages.registerCodeActionsProvider(documentSelector, codeActionProvider));
8282
localDisposables.add(reportDiagnostics(server, advisor));
8383
localDisposables.add(forwardChanges(server));
84+
localDisposables.add(trackVirtualDocuments(server, eventStream));
8485
localDisposables.add(vscode.languages.registerFoldingRangeProvider(documentSelector, new StructureProvider(server)));
8586
}));
8687

src/omnisharp/loggingEvents.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,10 @@ export class DotNetTestsInClassDebugStart implements BaseEvent {
147147
constructor(public className: string) { }
148148
}
149149

150+
export class DocumentSynchronizationFailure implements BaseEvent {
151+
constructor(public documentPath: string, public errorMessage: string) { }
152+
}
153+
150154
export class DebuggerPrerequisiteFailure extends EventWithMessage { }
151155
export class DebuggerPrerequisiteWarning extends EventWithMessage { }
152156
export class CommandDotNetRestoreProgress extends EventWithMessage { }
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
import * as vscode from 'vscode';
7+
8+
import { should, expect } from 'chai';
9+
import { activateCSharpExtension } from './integrationHelpers';
10+
import testAssetWorkspace from './testAssets/testAssetWorkspace';
11+
import { IDisposable } from '../../src/Disposable';
12+
13+
const chai = require('chai');
14+
chai.use(require('chai-arrays'));
15+
chai.use(require('chai-fs'));
16+
17+
suite(`Virtual Document Tracking ${testAssetWorkspace.description}`, function () {
18+
let virtualScheme: string = "virtual";
19+
let virtualDocumentRegistration: IDisposable;
20+
21+
suiteSetup(async function () {
22+
should();
23+
await testAssetWorkspace.restore();
24+
await activateCSharpExtension();
25+
26+
let virtualCSharpDocumentProvider = new VirtualCSharpDocumentProvider();
27+
virtualDocumentRegistration = vscode.workspace.registerTextDocumentContentProvider(virtualScheme, virtualCSharpDocumentProvider);
28+
});
29+
30+
suiteTeardown(async () => {
31+
await testAssetWorkspace.cleanupWorkspace();
32+
virtualDocumentRegistration.dispose();
33+
});
34+
35+
test("Virtual documents are operated on.", async () => {
36+
let virtualUri = vscode.Uri.parse(`${virtualScheme}://${testAssetWorkspace.projects[0].projectDirectoryPath}/_virtualFile.cs`);
37+
await vscode.workspace.openTextDocument(virtualUri);
38+
39+
let position = new vscode.Position(2, 4);
40+
let completionItems = <vscode.CompletionList>await vscode.commands.executeCommand("vscode.executeCompletionItemProvider", virtualUri, position);
41+
42+
expect(completionItems.items).to.satisfy(() => {
43+
let item = completionItems.items.find(item => {
44+
return item.label === "while";
45+
});
46+
47+
return item;
48+
});
49+
});
50+
});
51+
52+
class VirtualCSharpDocumentProvider implements vscode.TextDocumentContentProvider {
53+
onDidChange?: vscode.Event<vscode.Uri>;
54+
55+
provideTextDocumentContent(uri: vscode.Uri, token: vscode.CancellationToken): vscode.ProviderResult<string> {
56+
return `namespace Test
57+
{
58+
59+
}`;
60+
}
61+
}

0 commit comments

Comments
 (0)