diff --git a/src/vs/workbench/api/node/extHost.api.impl.ts b/src/vs/workbench/api/node/extHost.api.impl.ts index a39cdf3556e..feb0e727eb6 100644 --- a/src/vs/workbench/api/node/extHost.api.impl.ts +++ b/src/vs/workbench/api/node/extHost.api.impl.ts @@ -470,7 +470,7 @@ export function createApiFactory( return extHostDocuments.onDidSaveDocument(listener, thisArgs, disposables); }, onWillSaveTextDocument: (listener, thisArgs?, disposables?) => { - return extHostDocumentSaveParticipant.onWillSaveTextDocumentEvent(listener, thisArgs, disposables); + return extHostDocumentSaveParticipant.getOnWillSaveTextDocumentEvent(extension)(listener, thisArgs, disposables); }, onDidChangeConfiguration: (listener: (_: any) => any, thisArgs?: any, disposables?: extHostTypes.Disposable[]) => { return extHostConfiguration.onDidChangeConfiguration(listener, thisArgs, disposables); diff --git a/src/vs/workbench/api/node/extHostDocumentSaveParticipant.ts b/src/vs/workbench/api/node/extHostDocumentSaveParticipant.ts index 479ac970cd4..da949bda937 100644 --- a/src/vs/workbench/api/node/extHostDocumentSaveParticipant.ts +++ b/src/vs/workbench/api/node/extHostDocumentSaveParticipant.ts @@ -15,12 +15,15 @@ import { ExtHostDocuments } from 'vs/workbench/api/node/extHostDocuments'; import { SaveReason } from 'vs/workbench/services/textfile/common/textfiles'; import * as vscode from 'vscode'; import { LinkedList } from 'vs/base/common/linkedList'; +import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; + +type Listener = [Function, any, IExtensionDescription]; export class ExtHostDocumentSaveParticipant implements ExtHostDocumentSaveParticipantShape { private _documents: ExtHostDocuments; private _mainThreadEditors: MainThreadEditorsShape; - private _callbacks = new LinkedList<[Function, any]>(); + private _callbacks = new LinkedList(); private _badListeners = new WeakMap(); private _thresholds: { timeout: number; errors: number; }; @@ -34,9 +37,9 @@ export class ExtHostDocumentSaveParticipant implements ExtHostDocumentSavePartic this._callbacks.clear(); } - get onWillSaveTextDocumentEvent(): Event { + getOnWillSaveTextDocumentEvent(extension: IExtensionDescription): Event { return (listener, thisArg, disposables) => { - const remove = this._callbacks.push([listener, thisArg]); + const remove = this._callbacks.push([listener, thisArg, extension]); const result = { dispose: remove }; if (Array.isArray(disposables)) { disposables.push(result); @@ -51,7 +54,7 @@ export class ExtHostDocumentSaveParticipant implements ExtHostDocumentSavePartic let didTimeout = false; let didTimeoutHandle = setTimeout(() => didTimeout = true, this._thresholds.timeout); - const promise = sequence(entries.map(([fn, thisArg]) => { + const promise = sequence(entries.map(listener => { return () => { if (didTimeout) { @@ -60,14 +63,13 @@ export class ExtHostDocumentSaveParticipant implements ExtHostDocumentSavePartic } const document = this._documents.getDocumentData(resource).document; - return this._deliverEventAsyncAndBlameBadListeners(fn, thisArg, { document, reason: TextDocumentSaveReason.to(reason) }); + return this._deliverEventAsyncAndBlameBadListeners(listener, { document, reason: TextDocumentSaveReason.to(reason) }); }; })); - return always(promise, () => clearTimeout(didTimeoutHandle)); } - private _deliverEventAsyncAndBlameBadListeners(listener: Function, thisArg: any, stubEvent: vscode.TextDocumentWillSaveEvent): Promise { + private _deliverEventAsyncAndBlameBadListeners([listener, thisArg, extension]: Listener, stubEvent: vscode.TextDocumentWillSaveEvent): Promise { const errors = this._badListeners.get(listener); if (errors > this._thresholds.errors) { // bad listener - ignore diff --git a/src/vs/workbench/test/electron-browser/api/extHostDocumentSaveParticipant.test.ts b/src/vs/workbench/test/electron-browser/api/extHostDocumentSaveParticipant.test.ts index d937c8af433..6e802feca8b 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostDocumentSaveParticipant.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostDocumentSaveParticipant.test.ts @@ -16,12 +16,23 @@ import { OneGetThreadService } from './testThreadService'; import { SaveReason } from 'vs/workbench/services/textfile/common/textfiles'; import * as vscode from 'vscode'; import { mock } from 'vs/workbench/test/electron-browser/api/mock'; +import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; suite('ExtHostDocumentSaveParticipant', () => { let resource = URI.parse('foo:bar'); let mainThreadEditors = new class extends mock() { }; let documents: ExtHostDocuments; + let nullExtensionDescription: IExtensionDescription = { + id: 'nullExtensionDescription', + name: 'Null Extension Description', + publisher: 'vscode', + enableProposedApi: false, + engines: undefined, + extensionFolderPath: undefined, + isBuiltin: false, + version: undefined + }; setup(() => { const documentsAndEditors = new ExtHostDocumentsAndEditors(OneGetThreadService(null)); @@ -47,7 +58,7 @@ suite('ExtHostDocumentSaveParticipant', () => { const participant = new ExtHostDocumentSaveParticipant(documents, mainThreadEditors); let event: vscode.TextDocumentWillSaveEvent; - let sub = participant.onWillSaveTextDocumentEvent(function (e) { + let sub = participant.getOnWillSaveTextDocumentEvent(nullExtensionDescription)(function (e) { event = e; }); @@ -64,7 +75,7 @@ suite('ExtHostDocumentSaveParticipant', () => { const participant = new ExtHostDocumentSaveParticipant(documents, mainThreadEditors); let event: vscode.TextDocumentWillSaveEvent; - let sub = participant.onWillSaveTextDocumentEvent(function (e) { + let sub = participant.getOnWillSaveTextDocumentEvent(nullExtensionDescription)(function (e) { event = e; }); @@ -79,7 +90,7 @@ suite('ExtHostDocumentSaveParticipant', () => { test('event delivery, bad listener', () => { const participant = new ExtHostDocumentSaveParticipant(documents, mainThreadEditors); - let sub = participant.onWillSaveTextDocumentEvent(function (e) { + let sub = participant.getOnWillSaveTextDocumentEvent(nullExtensionDescription)(function (e) { throw new Error('💀'); }); @@ -94,11 +105,11 @@ suite('ExtHostDocumentSaveParticipant', () => { test('event delivery, bad listener doesn\'t prevent more events', () => { const participant = new ExtHostDocumentSaveParticipant(documents, mainThreadEditors); - let sub1 = participant.onWillSaveTextDocumentEvent(function (e) { + let sub1 = participant.getOnWillSaveTextDocumentEvent(nullExtensionDescription)(function (e) { throw new Error('💀'); }); let event: vscode.TextDocumentWillSaveEvent; - let sub2 = participant.onWillSaveTextDocumentEvent(function (e) { + let sub2 = participant.getOnWillSaveTextDocumentEvent(nullExtensionDescription)(function (e) { event = e; }); @@ -114,11 +125,11 @@ suite('ExtHostDocumentSaveParticipant', () => { const participant = new ExtHostDocumentSaveParticipant(documents, mainThreadEditors); let counter = 0; - let sub1 = participant.onWillSaveTextDocumentEvent(function (event) { + let sub1 = participant.getOnWillSaveTextDocumentEvent(nullExtensionDescription)(function (event) { assert.equal(counter++, 0); }); - let sub2 = participant.onWillSaveTextDocumentEvent(function (event) { + let sub2 = participant.getOnWillSaveTextDocumentEvent(nullExtensionDescription)(function (event) { assert.equal(counter++, 1); }); @@ -132,7 +143,7 @@ suite('ExtHostDocumentSaveParticipant', () => { const participant = new ExtHostDocumentSaveParticipant(documents, mainThreadEditors, { timeout: 5, errors: 1 }); let callCount = 0; - let sub = participant.onWillSaveTextDocumentEvent(function (event) { + let sub = participant.getOnWillSaveTextDocumentEvent(nullExtensionDescription)(function (event) { callCount += 1; throw new Error('boom'); }); @@ -150,17 +161,17 @@ suite('ExtHostDocumentSaveParticipant', () => { const participant = new ExtHostDocumentSaveParticipant(documents, mainThreadEditors, { timeout: 20, errors: 5 }); let callCount = 0; - let sub1 = participant.onWillSaveTextDocumentEvent(function (event) { + let sub1 = participant.getOnWillSaveTextDocumentEvent(nullExtensionDescription)(function (event) { callCount += 1; event.waitUntil(TPromise.timeout(17)); }); - let sub2 = participant.onWillSaveTextDocumentEvent(function (event) { + let sub2 = participant.getOnWillSaveTextDocumentEvent(nullExtensionDescription)(function (event) { callCount += 1; event.waitUntil(TPromise.timeout(17)); }); - let sub3 = participant.onWillSaveTextDocumentEvent(function (event) { + let sub3 = participant.getOnWillSaveTextDocumentEvent(nullExtensionDescription)(function (event) { callCount += 1; }); @@ -177,7 +188,7 @@ suite('ExtHostDocumentSaveParticipant', () => { test('event delivery, waitUntil', () => { const participant = new ExtHostDocumentSaveParticipant(documents, mainThreadEditors); - let sub = participant.onWillSaveTextDocumentEvent(function (event) { + let sub = participant.getOnWillSaveTextDocumentEvent(nullExtensionDescription)(function (event) { event.waitUntil(TPromise.timeout(10)); event.waitUntil(TPromise.timeout(10)); @@ -193,7 +204,7 @@ suite('ExtHostDocumentSaveParticipant', () => { test('event delivery, waitUntil must be called sync', () => { const participant = new ExtHostDocumentSaveParticipant(documents, mainThreadEditors); - let sub = participant.onWillSaveTextDocumentEvent(function (event) { + let sub = participant.getOnWillSaveTextDocumentEvent(nullExtensionDescription)(function (event) { event.waitUntil(new TPromise((resolve, reject) => { setTimeout(() => { @@ -216,7 +227,7 @@ suite('ExtHostDocumentSaveParticipant', () => { test('event delivery, waitUntil will timeout', () => { const participant = new ExtHostDocumentSaveParticipant(documents, mainThreadEditors, { timeout: 5, errors: 3 }); - let sub = participant.onWillSaveTextDocumentEvent(function (event) { + let sub = participant.getOnWillSaveTextDocumentEvent(nullExtensionDescription)(function (event) { event.waitUntil(TPromise.timeout(15)); }); @@ -231,12 +242,12 @@ suite('ExtHostDocumentSaveParticipant', () => { test('event delivery, waitUntil failure handling', () => { const participant = new ExtHostDocumentSaveParticipant(documents, mainThreadEditors); - let sub1 = participant.onWillSaveTextDocumentEvent(function (e) { + let sub1 = participant.getOnWillSaveTextDocumentEvent(nullExtensionDescription)(function (e) { e.waitUntil(TPromise.wrapError(new Error('dddd'))); }); let event: vscode.TextDocumentWillSaveEvent; - let sub2 = participant.onWillSaveTextDocumentEvent(function (e) { + let sub2 = participant.getOnWillSaveTextDocumentEvent(nullExtensionDescription)(function (e) { event = e; }); @@ -257,7 +268,7 @@ suite('ExtHostDocumentSaveParticipant', () => { } }); - let sub = participant.onWillSaveTextDocumentEvent(function (e) { + let sub = participant.getOnWillSaveTextDocumentEvent(nullExtensionDescription)(function (e) { e.waitUntil(TPromise.as([TextEdit.insert(new Position(0, 0), 'bar')])); e.waitUntil(TPromise.as([TextEdit.setEndOfLine(EndOfLine.CRLF)])); }); @@ -280,7 +291,7 @@ suite('ExtHostDocumentSaveParticipant', () => { } }); - let sub = participant.onWillSaveTextDocumentEvent(function (e) { + let sub = participant.getOnWillSaveTextDocumentEvent(nullExtensionDescription)(function (e) { // concurrent change from somewhere documents.$acceptModelChanged(resource.toString(), { @@ -330,7 +341,7 @@ suite('ExtHostDocumentSaveParticipant', () => { const document = documents.getDocumentData(resource).document; - let sub1 = participant.onWillSaveTextDocumentEvent(function (e) { + let sub1 = participant.getOnWillSaveTextDocumentEvent(nullExtensionDescription)(function (e) { // the document state we started with assert.equal(document.version, 1); assert.equal(document.getText(), 'foo'); @@ -338,7 +349,7 @@ suite('ExtHostDocumentSaveParticipant', () => { e.waitUntil(TPromise.as([TextEdit.insert(new Position(0, 0), 'bar')])); }); - let sub2 = participant.onWillSaveTextDocumentEvent(function (e) { + let sub2 = participant.getOnWillSaveTextDocumentEvent(nullExtensionDescription)(function (e) { // the document state AFTER the first listener kicked in assert.equal(document.version, 2); assert.equal(document.getText(), 'barfoo');