From f484123616aa323b15c508390081b5651375aa71 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 8 Jan 2016 11:02:39 +0100 Subject: [PATCH] refine API, add closeTextDocument call --- .../vscode-api-tests/src/workspace.test.ts | 95 +++++++++++++++---- src/vs/vscode.d.ts | 14 ++- .../workbench/api/common/extHost.api.impl.ts | 3 + .../workbench/api/common/extHostDocuments.ts | 48 ++++++---- 4 files changed, 122 insertions(+), 38 deletions(-) diff --git a/extensions/vscode-api-tests/src/workspace.test.ts b/extensions/vscode-api-tests/src/workspace.test.ts index aff25c2ae4f..60b492a9a32 100644 --- a/extensions/vscode-api-tests/src/workspace.test.ts +++ b/extensions/vscode-api-tests/src/workspace.test.ts @@ -122,11 +122,8 @@ suite('workspace-namespace', () => { test('registerTextDocumentContentProvider, simple', function() { let registration = workspace.registerTextDocumentContentProvider('foo', { - open(uri) { + provideTextDocumentContent(uri) { return uri.toString(); - }, - close() { - // nothing } }); @@ -143,29 +140,26 @@ suite('workspace-namespace', () => { // built-in assert.throws(function() { - workspace.registerTextDocumentContentProvider('untitled', { open() { return null; }, close() { } }); + workspace.registerTextDocumentContentProvider('untitled', { provideTextDocumentContent() { return null; } }); }); // built-in assert.throws(function() { - workspace.registerTextDocumentContentProvider('file', { open() { return null; }, close() { } }); + workspace.registerTextDocumentContentProvider('file', { provideTextDocumentContent() { return null; } }); }); // duplicate registration let registration = workspace.registerTextDocumentContentProvider('foo', { - open(uri) { + provideTextDocumentContent(uri) { return uri.toString(); - }, - close() { - // nothing } }); assert.throws(function() { - workspace.registerTextDocumentContentProvider('foo', { open() { return null; }, close() { } }); + workspace.registerTextDocumentContentProvider('foo', { provideTextDocumentContent() { return null; } }); }); // unregister & register registration.dispose(); - registration = workspace.registerTextDocumentContentProvider('foo', { open() { return null; }, close() { } }); + registration = workspace.registerTextDocumentContentProvider('foo', { provideTextDocumentContent() { return null; } }); registration.dispose(); // missing scheme @@ -178,13 +172,9 @@ suite('workspace-namespace', () => { test('registerTextDocumentContentProvider, show virtual document', function() { - // duplicate registration let registration = workspace.registerTextDocumentContentProvider('foo', { - open(uri) { + provideTextDocumentContent(uri) { return 'I am virtual'; - }, - close() { - // nothing } }); @@ -198,6 +188,77 @@ suite('workspace-namespace', () => { }); }); + test('registerTextDocumentContentProvider, open/close document 1/2', function() { + + let registration = workspace.registerTextDocumentContentProvider('foo', { + provideTextDocumentContent(uri) { + return 'I am virtual'; + } + }); + + const uri = Uri.parse('foo://testing/path'); + + return workspace.openTextDocument(uri).then(doc => { + + assert.ok(workspace.textDocuments.some(doc => doc.uri.toString() === uri.toString())); + + return workspace.closeTextDocument(doc).then(value => { + assert.ok(value); + assert.ok(!workspace.textDocuments.some(doc => doc.uri.toString() === uri.toString())); + registration.dispose(); + }); + }); + }); + + test('registerTextDocumentContentProvider, open/close document 2/2', function() { + + let registration = workspace.registerTextDocumentContentProvider('foo', { + provideTextDocumentContent(uri) { + return 'I am virtual'; + } + }); + + const uri = Uri.parse('foo://testing/path'); + + return workspace.openTextDocument(uri).then(first => { + + assert.ok(workspace.textDocuments.some(doc => doc.uri.toString() === uri.toString())); + + return workspace.openTextDocument(uri).then(second => { + registration.dispose(); + assert.ok(first === second); + + return workspace.closeTextDocument(first).then(value => { + assert.ok(value); + + // should we reference count or not? + // assert.ok(workspace.textDocuments.some(doc => doc.uri.toString() === uri.toString())); + }); + }); + }); + }); + + test('registerTextDocumentContentProvider, open/open document', function() { + + let callCount = 0; + let registration = workspace.registerTextDocumentContentProvider('foo', { + provideTextDocumentContent(uri) { + callCount += 1; + return 'I am virtual'; + } + }); + + const uri = Uri.parse('foo://testing/path'); + + return Promise.all([workspace.openTextDocument(uri), workspace.openTextDocument(uri)]).then(docs => { + let [first, second] = docs; + assert.ok(first === second); + assert.ok(workspace.textDocuments.some(doc => doc.uri.toString() === uri.toString())); + assert.equal(callCount, 1); + registration.dispose(); + }); + }); + test('findFiles', () => { return workspace.findFiles('*.js', null).then((res) => { assert.equal(res.length, 1); diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index 99f0c78516c..36f61c6061d 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -1025,12 +1025,13 @@ declare namespace vscode { onDidChange?: Event; /** + * Provide textual content for a given uri. * + * @param uri An uri which scheme matches the scheme this provider was [registered](#workspace.registerTextDocumentContentProvider) for. + * @param token A cancellation token. + * @return A string or a thenable that resolves to such. */ - open(uri: Uri, token: CancellationToken): string | Thenable; - - // todo@joh make this optional? - close(uri: Uri, token: CancellationToken): any | Thenable; + provideTextDocumentContent(uri: Uri, token: CancellationToken): string | Thenable; } /** @@ -3020,6 +3021,11 @@ declare namespace vscode { */ export function openTextDocument(fileName: string): Thenable; + /** + * + */ + export function closeTextDocument(document: TextDocument): Thenable; + /** * */ diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index a1b5619ec80..9bf9020a937 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -255,6 +255,9 @@ export class ExtHostAPIImplementation { openTextDocument(uriOrFileName:vscode.Uri | string) { return pluginHostDocuments.openDocument(uriOrFileName); }, + closeTextDocument(document: vscode.TextDocument) { + return pluginHostDocuments.closeDocument(document); + }, registerTextDocumentContentProvider(scheme: string, provider: vscode.TextDocumentContentProvider) { return pluginHostDocuments.registerTextDocumentContentProvider(scheme, provider); }, diff --git a/src/vs/workbench/api/common/extHostDocuments.ts b/src/vs/workbench/api/common/extHostDocuments.ts index 14039b20911..72a843c7968 100644 --- a/src/vs/workbench/api/common/extHostDocuments.ts +++ b/src/vs/workbench/api/common/extHostDocuments.ts @@ -66,6 +66,7 @@ export class ExtHostModelService { public onDidSaveDocument: Event; private _documents: { [modelUri: string]: ExtHostDocument; }; + private _loadingDocuments: { [modelUri: string]: TPromise }; private _documentContentProviders: { [scheme: string]: vscode.TextDocumentContentProvider }; private _proxy: MainThreadDocuments; @@ -86,6 +87,7 @@ export class ExtHostModelService { this.onDidSaveDocument = this._onDidSaveDocumentEventEmitter.event; this._documents = Object.create(null); + this._loadingDocuments = Object.create(null); this._documentContentProviders = Object.create(null); } @@ -119,12 +121,26 @@ export class ExtHostModelService { if (cached) { return TPromise.as(cached); } - return this._proxy._tryOpenDocument(uri).then(() => { + + let promise = this._loadingDocuments[uri.toString()]; + if (promise) { + return promise; + } + + return this._loadingDocuments[uri.toString()] = this._proxy._tryOpenDocument(uri).then(() => { + delete this._loadingDocuments[uri.toString()]; return this._documents[uri.toString()]; + }, err => { + delete this._loadingDocuments[uri.toString()]; + return TPromise.wrapError(err); }); } - registerTextDocumentContentProvider(scheme: string, provider: vscode.TextDocumentContentProvider): vscode.Disposable { + public closeDocument(document: vscode.TextDocument): TPromise { + return this._proxy._tryCloseDocument( document.uri); + } + + public registerTextDocumentContentProvider(scheme: string, provider: vscode.TextDocumentContentProvider): vscode.Disposable { if (scheme === 'file' || scheme === 'untitled' || this._documentContentProviders[scheme]) { throw new Error(`scheme '${scheme}' already registered`); } @@ -132,21 +148,17 @@ export class ExtHostModelService { return new Disposable(() => delete this._documentContentProviders[scheme]); } - $openTextDocumentContent(uri: URI): TPromise { + $provideTextDocumentContent(uri: URI): TPromise { const provider = this._documentContentProviders[uri.scheme]; if (!provider) { return TPromise.wrapError(`unsupported uri-scheme: ${uri.scheme}`); } - // todo@joh protected for !string results, slow provider etc - return asWinJsPromise(token => provider.open(uri, token)); - } - - $closeTextDocumentContent(uri: URI): TPromise { - const provider = this._documentContentProviders[uri.scheme]; - if (!provider) { - return TPromise.wrapError(`unsupported uri-scheme: ${uri.scheme}`); - } - return asWinJsPromise(token => provider.close(uri, token)); + return asWinJsPromise(token => provider.provideTextDocumentContent(uri, token)).then(value => { + if (typeof value !== 'string') { + return TPromise.wrapError('received illegal value from text document provider'); + } + return value; + }); } public _acceptModelAdd(data:IModelAddedData): void { @@ -731,10 +743,7 @@ export class MainThreadDocuments { return TPromise.as(true); } - return this._proxy.$openTextDocumentContent(uri).then(value => { - if (typeof value !== 'string') { - return TPromise.wrapError('illegal value'); - } + return this._proxy.$provideTextDocumentContent(uri).then(value => { const firstLineText = value.substr(0, 1 + value.search(/\r?\n/)); const mode = this._modeService.getOrCreateModeByFilenameOrFirstLine(uri.fsPath, firstLineText); return this._modelService.createModel(value, mode, uri); @@ -743,4 +752,9 @@ export class MainThreadDocuments { return true; }); } + + _tryCloseDocument(uri: URI): TPromise { + this._modelService.destroyModel(uri); + return TPromise.as(true); + } }