diff --git a/src/vs/workbench/api/node/extHostDocumentSaveParticipant.ts b/src/vs/workbench/api/node/extHostDocumentSaveParticipant.ts index 7bd8bd07c8c..50f1aa656dc 100644 --- a/src/vs/workbench/api/node/extHostDocumentSaveParticipant.ts +++ b/src/vs/workbench/api/node/extHostDocumentSaveParticipant.ts @@ -10,20 +10,26 @@ import URI from 'vs/base/common/uri'; import {sequence} from 'vs/base/common/async'; import {illegalState} from 'vs/base/common/errors'; import {TPromise} from 'vs/base/common/winjs.base'; +import {MainThreadWorkspaceShape} from 'vs/workbench/api/node/extHost.protocol'; +import {fromRange} from 'vs/workbench/api/node/extHostTypeConverters'; +import {IResourceEdit} from 'vs/editor/common/services/bulkEdit'; import {ExtHostDocuments} from 'vs/workbench/api/node/extHostDocuments'; export interface TextDocumentWillSaveEvent { document: vscode.TextDocument; + pushEdits(edits: vscode.TextEdit[]): void; waitUntil(t: Thenable): void; } export class ExtHostDocumentSaveParticipant { - private _callbacks = new CallbackList(); private _documents: ExtHostDocuments; + private _workspace: MainThreadWorkspaceShape; + private _callbacks = new CallbackList(); - constructor(documents: ExtHostDocuments) { + constructor(documents: ExtHostDocuments, workspace: MainThreadWorkspaceShape) { this._documents = documents; + this._workspace = workspace; } dispose(): void { @@ -55,22 +61,58 @@ export class ExtHostDocumentSaveParticipant { private _deliverEventAsync(listener: Function, thisArg: any, document: vscode.TextDocument): TPromise { const promises: TPromise[] = []; + const resourceEdits: IResourceEdit[] = []; - const event: TextDocumentWillSaveEvent = Object.freeze({ + const {version} = document; + + const event = Object.freeze( { document, + pushEdits(edits) { + if (Object.isFrozen(resourceEdits)) { + throw illegalState('pushEdits can not be called anymore'); + } + for (const {newText, range} of edits) { + resourceEdits.push({ + newText, + range: fromRange(range), + resource: document.uri, + }); + } + }, waitUntil(p: Thenable) { if (Object.isFrozen(promises)) { - throw illegalState('waitUntil can not be called async'); + throw illegalState('waitUntil can not be called anymore'); } promises.push(TPromise.wrap(p)); } }); try { + // fire event listener.apply(thisArg, [event]); } finally { + // freeze promises after event call Object.freeze(promises); - return TPromise.join(promises).then(() => void 0, err => void 0 /* ignore */); + + return TPromise.join(promises).then(() => { + // freeze edits after async/sync is done + Object.freeze(resourceEdits); + + if (resourceEdits.length === 0) { + return; + } + + if (version !== document.version) { + // TODO@joh - fail? + return; + } + + // apply edits iff any + return this._workspace.$applyWorkspaceEdit(resourceEdits); + + }, err => { + // ignore error + }); } } } \ No newline at end of file diff --git a/src/vs/workbench/test/node/api/extHostDocumentSaveParticipant.test.ts b/src/vs/workbench/test/node/api/extHostDocumentSaveParticipant.test.ts index 19863f67735..bd4285b2ebd 100644 --- a/src/vs/workbench/test/node/api/extHostDocumentSaveParticipant.test.ts +++ b/src/vs/workbench/test/node/api/extHostDocumentSaveParticipant.test.ts @@ -8,13 +8,17 @@ import * as assert from 'assert'; import URI from 'vs/base/common/uri'; import {TPromise} from 'vs/base/common/winjs.base'; import {ExtHostDocuments} from 'vs/workbench/api/node/extHostDocuments'; +import {TextEdit, Position} from 'vs/workbench/api/node/extHostTypes'; +import {MainThreadWorkspaceShape} from 'vs/workbench/api/node/extHost.protocol'; import {ExtHostDocumentSaveParticipant, TextDocumentWillSaveEvent} from 'vs/workbench/api/node/extHostDocumentSaveParticipant'; import {OneGetThreadService} from './testThreadService'; import * as EditorCommon from 'vs/editor/common/editorCommon'; +import {IResourceEdit} from 'vs/editor/common/services/bulkEdit'; suite('ExtHostDocumentSaveParticipant', () => { let resource = URI.parse('foo:bar'); + let workspace = new class extends MainThreadWorkspaceShape { }; let documents: ExtHostDocuments; setup(() => { @@ -41,12 +45,12 @@ suite('ExtHostDocumentSaveParticipant', () => { }); test('no listeners, no problem', () => { - const participant = new ExtHostDocumentSaveParticipant(documents); + const participant = new ExtHostDocumentSaveParticipant(documents, workspace); return participant.$participateInSave(resource).then(() => assert.ok(true)); }); test('event delivery', () => { - const participant = new ExtHostDocumentSaveParticipant(documents); + const participant = new ExtHostDocumentSaveParticipant(documents, workspace); let event: TextDocumentWillSaveEvent; let sub = participant.onWillSaveTextDocumentEvent(function (e) { @@ -62,7 +66,7 @@ suite('ExtHostDocumentSaveParticipant', () => { }); test('event delivery, immutable', () => { - const participant = new ExtHostDocumentSaveParticipant(documents); + const participant = new ExtHostDocumentSaveParticipant(documents, workspace); let event: TextDocumentWillSaveEvent; let sub = participant.onWillSaveTextDocumentEvent(function (e) { @@ -78,7 +82,7 @@ suite('ExtHostDocumentSaveParticipant', () => { }); test('event delivery, in subscriber order', () => { - const participant = new ExtHostDocumentSaveParticipant(documents); + const participant = new ExtHostDocumentSaveParticipant(documents, workspace); let counter = 0; let sub1 = participant.onWillSaveTextDocumentEvent(function (event) { @@ -96,7 +100,7 @@ suite('ExtHostDocumentSaveParticipant', () => { }); test('event delivery, waitUntil', () => { - const participant = new ExtHostDocumentSaveParticipant(documents); + const participant = new ExtHostDocumentSaveParticipant(documents, workspace); let sub = participant.onWillSaveTextDocumentEvent(function (event) { @@ -112,7 +116,7 @@ suite('ExtHostDocumentSaveParticipant', () => { }); test('event delivery, waitUntil must be called sync', () => { - const participant = new ExtHostDocumentSaveParticipant(documents); + const participant = new ExtHostDocumentSaveParticipant(documents, workspace); let sub = participant.onWillSaveTextDocumentEvent(function (event) { @@ -135,7 +139,7 @@ suite('ExtHostDocumentSaveParticipant', () => { }); test('event delivery, waitUntil failure handling', () => { - const participant = new ExtHostDocumentSaveParticipant(documents); + const participant = new ExtHostDocumentSaveParticipant(documents, workspace); let sub1 = participant.onWillSaveTextDocumentEvent(function (e) { e.waitUntil(TPromise.wrapError('dddd')); @@ -153,4 +157,24 @@ suite('ExtHostDocumentSaveParticipant', () => { }); }); + test('event delivery, pushEdits sync', () => { + + let edits: IResourceEdit[]; + const participant = new ExtHostDocumentSaveParticipant(documents, new class extends MainThreadWorkspaceShape { + $applyWorkspaceEdit(_edits) { + edits = _edits; + return TPromise.as(true); + } + }); + + let sub = participant.onWillSaveTextDocumentEvent(function (e) { + e.pushEdits([TextEdit.insert(new Position(0, 0), 'bar')]); + }); + + return participant.$participateInSave(resource).then(() => { + sub.dispose(); + + assert.equal(edits.length, 1); + }); + }); });