Skip to content

Commit 1fd95d5

Browse files
authored
Merge pull request microsoft#83822 from microsoft/joh/willRename
Propose API for [onWill|onDid][Create|Delete|Rename]
2 parents ec7e863 + 073735c commit 1fd95d5

11 files changed

Lines changed: 278 additions & 100 deletions

File tree

extensions/typescript-language-features/src/features/updatePathsOnRename.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,8 @@ class UpdateImportsOnFileRenameHandler extends Disposable {
5757
) {
5858
super();
5959

60-
this._register(vscode.workspace.onDidRenameFile(async ({ newUri, oldUri }) => {
60+
this._register(vscode.workspace.onDidRenameFiles(async (e) => {
61+
const [{ newUri, oldUri }] = e.renamed;
6162
const newFilePath = this.client.toPath(newUri);
6263
if (!newFilePath) {
6364
return;

src/vs/base/common/event.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -688,7 +688,7 @@ export class AsyncEmitter<T extends IWaitUntil> extends Emitter<T> {
688688
// freeze thenables-collection to enforce sync-calls to
689689
// wait until and then wait for all thenables to resolve
690690
Object.freeze(thenables);
691-
await Promise.all(thenables);
691+
await Promise.all(thenables).catch(e => onUnexpectedError(e));
692692
}
693693
}
694694
}

src/vs/base/test/common/event.test.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,42 @@ suite('AsyncEmitter', function () {
350350
}));
351351
assert.ok(done);
352352
});
353+
354+
test('catch errors', async function () {
355+
const origErrorHandler = Errors.errorHandler.getUnexpectedErrorHandler();
356+
Errors.setUnexpectedErrorHandler(() => null);
357+
358+
interface E extends IWaitUntil {
359+
foo: boolean;
360+
}
361+
362+
let globalState = 0;
363+
let emitter = new AsyncEmitter<E>();
364+
365+
emitter.event(e => {
366+
globalState += 1;
367+
e.waitUntil(new Promise((_r, reject) => reject(new Error())));
368+
});
369+
370+
emitter.event(e => {
371+
globalState += 1;
372+
e.waitUntil(timeout(10));
373+
});
374+
375+
await emitter.fireAsync(thenables => ({
376+
foo: true,
377+
waitUntil(t) {
378+
thenables.push(t);
379+
}
380+
})).then(() => {
381+
assert.equal(globalState, 2);
382+
}).catch(e => {
383+
console.log(e);
384+
assert.ok(false);
385+
});
386+
387+
Errors.setUnexpectedErrorHandler(origErrorHandler);
388+
});
353389
});
354390

355391
suite('PausableEmitter', function () {

src/vs/platform/files/test/node/diskFileService.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,7 @@ suite('Disk File Service', function () {
428428
await service.del(source.resource);
429429

430430
assert.equal(existsSync(source.resource.fsPath), false);
431+
431432
assert.ok(event!);
432433
assert.equal(event!.resource.fsPath, resource.fsPath);
433434
assert.equal(event!.operation, FileOperation.DELETE);

src/vs/vscode.proposed.d.ts

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -708,20 +708,45 @@ declare module 'vscode' {
708708
//#endregion
709709

710710
//#region mjbvz,joh: https://github.com/Microsoft/vscode/issues/43768
711+
712+
export interface FileCreateEvent {
713+
readonly created: ReadonlyArray<Uri>;
714+
}
715+
716+
export interface FileWillCreateEvent {
717+
readonly creating: ReadonlyArray<Uri>;
718+
waitUntil(thenable: Thenable<any>): void;
719+
}
720+
721+
export interface FileDeleteEvent {
722+
readonly deleted: ReadonlyArray<Uri>;
723+
}
724+
725+
export interface FileWillDeleteEvent {
726+
readonly deleting: ReadonlyArray<Uri>;
727+
waitUntil(thenable: Thenable<any>): void;
728+
}
729+
711730
export interface FileRenameEvent {
712-
readonly oldUri: Uri;
713-
readonly newUri: Uri;
731+
readonly renamed: ReadonlyArray<{ oldUri: Uri, newUri: Uri }>;
714732
}
715733

716734
export interface FileWillRenameEvent {
717-
readonly oldUri: Uri;
718-
readonly newUri: Uri;
735+
readonly renaming: ReadonlyArray<{ oldUri: Uri, newUri: Uri }>;
719736
waitUntil(thenable: Thenable<WorkspaceEdit>): void;
720737
}
721738

722739
export namespace workspace {
723-
export const onWillRenameFile: Event<FileWillRenameEvent>;
724-
export const onDidRenameFile: Event<FileRenameEvent>;
740+
741+
export const onWillCreateFiles: Event<FileWillCreateEvent>;
742+
export const onDidCreateFiles: Event<FileCreateEvent>;
743+
744+
export const onWillDeleteFiles: Event<FileWillDeleteEvent>;
745+
export const onDidDeleteFiles: Event<FileDeleteEvent>;
746+
747+
export const onWillRenameFiles: Event<FileWillRenameEvent>;
748+
export const onDidRenameFiles: Event<FileRenameEvent>;
749+
725750
}
726751
//#endregion
727752

src/vs/workbench/api/browser/mainThreadFileSystemEventService.ts

Lines changed: 32 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,24 @@
33
* Licensed under the MIT License. See License.txt in the project root for license information.
44
*--------------------------------------------------------------------------------------------*/
55

6-
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
6+
import { DisposableStore } from 'vs/base/common/lifecycle';
77
import { FileChangeType, IFileService, FileOperation } from 'vs/platform/files/common/files';
88
import { extHostCustomer } from 'vs/workbench/api/common/extHostCustomers';
99
import { ExtHostContext, FileSystemEvents, IExtHostContext } from '../common/extHost.protocol';
1010
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
11+
import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress';
12+
import { localize } from 'vs/nls';
1113

1214
@extHostCustomer
1315
export class MainThreadFileSystemEventService {
1416

15-
private readonly _listener = new Array<IDisposable>();
17+
private readonly _listener = new DisposableStore();
1618

1719
constructor(
1820
extHostContext: IExtHostContext,
1921
@IFileService fileService: IFileService,
20-
@ITextFileService textfileService: ITextFileService,
22+
@ITextFileService textFileService: ITextFileService,
23+
@IProgressService progressService: IProgressService
2124
) {
2225

2326
const proxy = extHostContext.getProxy(ExtHostContext.ExtHostFileSystemEventService);
@@ -28,7 +31,7 @@ export class MainThreadFileSystemEventService {
2831
changed: [],
2932
deleted: []
3033
};
31-
fileService.onFileChanges(event => {
34+
this._listener.add(fileService.onFileChanges(event => {
3235
for (let change of event.changes) {
3336
switch (change.type) {
3437
case FileChangeType.ADDED:
@@ -47,22 +50,35 @@ export class MainThreadFileSystemEventService {
4750
events.created.length = 0;
4851
events.changed.length = 0;
4952
events.deleted.length = 0;
50-
}, undefined, this._listener);
53+
}));
5154

52-
// file operation events - (changes the editor makes)
53-
fileService.onAfterOperation(e => {
54-
if (e.isOperation(FileOperation.MOVE)) {
55-
proxy.$onFileRename(e.resource, e.target.resource);
56-
}
57-
}, undefined, this._listener);
5855

59-
textfileService.onWillMove(e => {
60-
const promise = proxy.$onWillRename(e.oldResource, e.newResource);
61-
e.waitUntil(promise);
62-
}, undefined, this._listener);
56+
// BEFORE file operation
57+
const messages = new Map<FileOperation, string>();
58+
messages.set(FileOperation.CREATE, localize('msg-create', "Running 'File Create' participants..."));
59+
messages.set(FileOperation.DELETE, localize('msg-delete', "Running 'File Delete' participants..."));
60+
messages.set(FileOperation.MOVE, localize('msg-rename', "Running 'File Rename' participants..."));
61+
62+
this._listener.add(textFileService.onWillRunOperation(e => {
63+
const p = progressService.withProgress({ location: ProgressLocation.Window }, progress => {
64+
65+
progress.report({ message: messages.get(e.operation) });
66+
67+
const p1 = proxy.$onWillRunFileOperation(e.operation, e.target, e.source);
68+
const p2 = new Promise((_resolve, reject) => {
69+
setTimeout(() => reject(new Error('timeout')), 5000);
70+
});
71+
return Promise.race([p1, p2]);
72+
});
73+
74+
e.waitUntil(p);
75+
}));
76+
77+
// AFTER file operation
78+
this._listener.add(textFileService.onDidRunOperation(e => proxy.$onDidRunFileOperation(e.operation, e.target, e.source)));
6379
}
6480

6581
dispose(): void {
66-
dispose(this._listener);
82+
this._listener.dispose();
6783
}
6884
}

src/vs/workbench/api/common/extHost.api.impl.ts

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -695,11 +695,27 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
695695
checkProposedApiEnabled(extension);
696696
return extHostLabelService.$registerResourceLabelFormatter(formatter);
697697
},
698-
onDidRenameFile: (listener: (e: vscode.FileRenameEvent) => any, thisArg?: any, disposables?: vscode.Disposable[]) => {
698+
onDidCreateFiles: (listener, thisArg, disposables) => {
699+
checkProposedApiEnabled(extension);
700+
return extHostFileSystemEvent.onDidCreateFile(listener, thisArg, disposables);
701+
},
702+
onDidDeleteFiles: (listener, thisArg, disposables) => {
703+
checkProposedApiEnabled(extension);
704+
return extHostFileSystemEvent.onDidDeleteFile(listener, thisArg, disposables);
705+
},
706+
onDidRenameFiles: (listener, thisArg, disposables) => {
699707
checkProposedApiEnabled(extension);
700708
return extHostFileSystemEvent.onDidRenameFile(listener, thisArg, disposables);
701709
},
702-
onWillRenameFile: (listener: (e: vscode.FileWillRenameEvent) => any, thisArg?: any, disposables?: vscode.Disposable[]) => {
710+
onWillCreateFiles: (listener: (e: vscode.FileWillCreateEvent) => any, thisArg?: any, disposables?: vscode.Disposable[]) => {
711+
checkProposedApiEnabled(extension);
712+
return extHostFileSystemEvent.getOnWillCreateFileEvent(extension)(listener, thisArg, disposables);
713+
},
714+
onWillDeleteFiles: (listener: (e: vscode.FileWillDeleteEvent) => any, thisArg?: any, disposables?: vscode.Disposable[]) => {
715+
checkProposedApiEnabled(extension);
716+
return extHostFileSystemEvent.getOnWillDeleteFileEvent(extension)(listener, thisArg, disposables);
717+
},
718+
onWillRenameFiles: (listener: (e: vscode.FileWillRenameEvent) => any, thisArg?: any, disposables?: vscode.Disposable[]) => {
703719
checkProposedApiEnabled(extension);
704720
return extHostFileSystemEvent.getOnWillRenameFileEvent(extension)(listener, thisArg, disposables);
705721
}

src/vs/workbench/api/common/extHost.protocol.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -914,10 +914,11 @@ export interface FileSystemEvents {
914914
changed: UriComponents[];
915915
deleted: UriComponents[];
916916
}
917+
917918
export interface ExtHostFileSystemEventServiceShape {
918919
$onFileEvent(events: FileSystemEvents): void;
919-
$onFileRename(oldUri: UriComponents, newUri: UriComponents): void;
920-
$onWillRename(oldUri: UriComponents, newUri: UriComponents): Promise<any>;
920+
$onWillRunFileOperation(operation: files.FileOperation, target: UriComponents, source: UriComponents | undefined): Promise<any>;
921+
$onDidRunFileOperation(operation: files.FileOperation, target: UriComponents, source: UriComponents | undefined): void;
921922
}
922923

923924
export interface ObjectIdentifier {

0 commit comments

Comments
 (0)