diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 396acc62af3256d16054850615c8acff1a73bd5e..99ece63c7de875364c94a5cbb850324bca385523 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -663,7 +663,7 @@ declare module 'vscode' { export interface FileWillRenameEvent { readonly oldUri: Uri; readonly newUri: Uri; - waitUntil(thenable: Thenable): void; + waitUntil(thenable: Thenable): void; } export namespace workspace { diff --git a/src/vs/workbench/api/node/extHost.api.impl.ts b/src/vs/workbench/api/node/extHost.api.impl.ts index 5c5443935286410b8e0176fd039112be4be3b8b7..609db622a2b71949b177a3cfd0d2854727359303 100644 --- a/src/vs/workbench/api/node/extHost.api.impl.ts +++ b/src/vs/workbench/api/node/extHost.api.impl.ts @@ -116,7 +116,7 @@ export function createApiFactory( const extHostDiagnostics = rpcProtocol.set(ExtHostContext.ExtHostDiagnostics, new ExtHostDiagnostics(rpcProtocol)); const extHostLanguageFeatures = rpcProtocol.set(ExtHostContext.ExtHostLanguageFeatures, new ExtHostLanguageFeatures(rpcProtocol, schemeTransformer, extHostDocuments, extHostCommands, extHostHeapService, extHostDiagnostics)); const extHostFileSystem = rpcProtocol.set(ExtHostContext.ExtHostFileSystem, new ExtHostFileSystem(rpcProtocol, extHostLanguageFeatures)); - const extHostFileSystemEvent = rpcProtocol.set(ExtHostContext.ExtHostFileSystemEventService, new ExtHostFileSystemEventService()); + const extHostFileSystemEvent = rpcProtocol.set(ExtHostContext.ExtHostFileSystemEventService, new ExtHostFileSystemEventService(rpcProtocol, extHostDocumentsAndEditors)); const extHostQuickOpen = rpcProtocol.set(ExtHostContext.ExtHostQuickOpen, new ExtHostQuickOpen(rpcProtocol, extHostWorkspace, extHostCommands)); const extHostTerminalService = rpcProtocol.set(ExtHostContext.ExtHostTerminalService, new ExtHostTerminalService(rpcProtocol, extHostConfiguration, extHostLogService)); const extHostDebugService = rpcProtocol.set(ExtHostContext.ExtHostDebugService, new ExtHostDebugService(rpcProtocol, extHostWorkspace, extensionService, extHostDocumentsAndEditors, extHostConfiguration, extHostTerminalService)); @@ -580,7 +580,7 @@ export function createApiFactory( return extHostFileSystemEvent.onDidRenameFile(listener, thisArg, disposables); }), onWillRenameFile: proposedApiFunction(extension, (listener, thisArg?, disposables?) => { - return extHostFileSystemEvent.onWillRenameFile(listener, thisArg, disposables); + return extHostFileSystemEvent.getOnWillRenameFileEvent(extension)(listener, thisArg, disposables); }) }; diff --git a/src/vs/workbench/api/node/extHostFileSystemEventService.ts b/src/vs/workbench/api/node/extHostFileSystemEventService.ts index 476a96a3c00130abd4059b51c270505bb46f4958..14424fe8b8056a74e5e028bf828fe2ab7087cf51 100644 --- a/src/vs/workbench/api/node/extHostFileSystemEventService.ts +++ b/src/vs/workbench/api/node/extHostFileSystemEventService.ts @@ -4,13 +4,17 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -import { Emitter, Event } from 'vs/base/common/event'; +import { flatten } from 'vs/base/common/arrays'; +import { AsyncEmitter, Emitter, Event } from 'vs/base/common/event'; import { IRelativePattern, parse } from 'vs/base/common/glob'; import URI, { UriComponents } from 'vs/base/common/uri'; -import * as vscode from 'vscode'; -import { ExtHostFileSystemEventServiceShape, FileSystemEvents } from './extHost.protocol'; -import { Disposable } from './extHostTypes'; import { TPromise } from 'vs/base/common/winjs.base'; +import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/node/extHostDocumentsAndEditors'; +import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; +import * as vscode from 'vscode'; +import { ExtHostFileSystemEventServiceShape, FileSystemEvents, IMainContext, MainContext, ResourceFileEditDto, ResourceTextEditDto, MainThreadTextEditorsShape } from './extHost.protocol'; +import * as typeConverter from './extHostTypeConverters'; +import { Disposable, WorkspaceEdit } from './extHostTypes'; class FileSystemWatcher implements vscode.FileSystemWatcher { @@ -94,16 +98,25 @@ class FileSystemWatcher implements vscode.FileSystemWatcher { } } +interface WillRenameListener { + extension: IExtensionDescription; + (e: vscode.FileWillRenameEvent): any; +} + export class ExtHostFileSystemEventService implements ExtHostFileSystemEventServiceShape { - private _onFileEvent = new Emitter(); - private _onDidRenameFile = new Emitter(); - private _onWillRenameFile = new Emitter(); + private readonly _onFileEvent = new Emitter(); + private readonly _onDidRenameFile = new Emitter(); + private readonly _onWillRenameFile = new AsyncEmitter(); readonly onDidRenameFile: Event = this._onDidRenameFile.event; - readonly onWillRenameFile: Event = this._onWillRenameFile.event; - constructor() { + constructor( + mainContext: IMainContext, + private readonly _extHostDocumentsAndEditors: ExtHostDocumentsAndEditors, + private readonly _mainThreadTextEditors: MainThreadTextEditorsShape = mainContext.getProxy(MainContext.MainThreadTextEditors) + ) { + // } public createFileSystemWatcher(globPattern: string | IRelativePattern, ignoreCreateEvents?: boolean, ignoreChangeEvents?: boolean, ignoreDeleteEvents?: boolean): vscode.FileSystemWatcher { @@ -118,23 +131,54 @@ export class ExtHostFileSystemEventService implements ExtHostFileSystemEventServ this._onDidRenameFile.fire(Object.freeze({ oldUri: URI.revive(oldUri), newUri: URI.revive(newUri) })); } - $onWillRename(oldUri: UriComponents, newUri: UriComponents): TPromise { - - let thenables: Thenable[] = []; + getOnWillRenameFileEvent(extension: IExtensionDescription): Event { + return (listener, thisArg, disposables) => { + let wrappedListener = function () { + listener.apply(thisArg, arguments); + }; + wrappedListener.extension = extension; + return this._onWillRenameFile.event(wrappedListener, undefined, disposables); + }; + } - this._onWillRenameFile.fire({ - oldUri: URI.revive(oldUri), - newUri: URI.revive(newUri), - waitUntil(thenable: Thenable): void { - if (Object.isFrozen(thenables)) { - throw new Error('waitUntil cannot be called async'); + $onWillRename(oldUriDto: UriComponents, newUriDto: UriComponents): TPromise { + const oldUri = URI.revive(oldUriDto); + const newUri = URI.revive(newUriDto); + + const edits: WorkspaceEdit[] = []; + return this._onWillRenameFile.fireAsync((bucket, listener) => { + return { + oldUri, + newUri, + waitUntil: (thenable: Thenable): void => { + if (Object.isFrozen(bucket)) { + throw new TypeError('waitUntil cannot be called async'); + } + const index = bucket.length; + const wrappedThenable = TPromise.as(thenable).then(result => { + // ignore all results except for WorkspaceEdits. Those + // are stored in a spare array + if (result instanceof WorkspaceEdit) { + edits[index] = result; + } + }); + bucket.push(wrappedThenable); + } + }; + }).then(() => { + if (edits.length === 0) { + return undefined; + } + // flatten all WorkspaceEdits collected via waitUntil-call + // and apply them in one go. + let allEdits = new Array<(ResourceFileEditDto | ResourceTextEditDto)[]>(); + for (let edit of edits) { + if (edit) { // sparse array + let { edits } = typeConverter.WorkspaceEdit.from(edit, this._extHostDocumentsAndEditors); + allEdits.push(edits); } - thenables.push(thenable.then(undefined, err => console.error(err))); } + return this._mainThreadTextEditors.$tryApplyWorkspaceEdit({ edits: flatten(allEdits) }); }); - - Object.freeze(thenables); - - return TPromise.join(thenables); } }