未验证 提交 1fd95d53 编写于 作者: J Johannes Rieken 提交者: GitHub

Merge pull request #83822 from microsoft/joh/willRename

Propose API for [onWill|onDid][Create|Delete|Rename]
......@@ -57,7 +57,8 @@ class UpdateImportsOnFileRenameHandler extends Disposable {
) {
super();
this._register(vscode.workspace.onDidRenameFile(async ({ newUri, oldUri }) => {
this._register(vscode.workspace.onDidRenameFiles(async (e) => {
const [{ newUri, oldUri }] = e.renamed;
const newFilePath = this.client.toPath(newUri);
if (!newFilePath) {
return;
......
......@@ -688,7 +688,7 @@ export class AsyncEmitter<T extends IWaitUntil> extends Emitter<T> {
// freeze thenables-collection to enforce sync-calls to
// wait until and then wait for all thenables to resolve
Object.freeze(thenables);
await Promise.all(thenables);
await Promise.all(thenables).catch(e => onUnexpectedError(e));
}
}
}
......
......@@ -350,6 +350,42 @@ suite('AsyncEmitter', function () {
}));
assert.ok(done);
});
test('catch errors', async function () {
const origErrorHandler = Errors.errorHandler.getUnexpectedErrorHandler();
Errors.setUnexpectedErrorHandler(() => null);
interface E extends IWaitUntil {
foo: boolean;
}
let globalState = 0;
let emitter = new AsyncEmitter<E>();
emitter.event(e => {
globalState += 1;
e.waitUntil(new Promise((_r, reject) => reject(new Error())));
});
emitter.event(e => {
globalState += 1;
e.waitUntil(timeout(10));
});
await emitter.fireAsync(thenables => ({
foo: true,
waitUntil(t) {
thenables.push(t);
}
})).then(() => {
assert.equal(globalState, 2);
}).catch(e => {
console.log(e);
assert.ok(false);
});
Errors.setUnexpectedErrorHandler(origErrorHandler);
});
});
suite('PausableEmitter', function () {
......
......@@ -428,6 +428,7 @@ suite('Disk File Service', function () {
await service.del(source.resource);
assert.equal(existsSync(source.resource.fsPath), false);
assert.ok(event!);
assert.equal(event!.resource.fsPath, resource.fsPath);
assert.equal(event!.operation, FileOperation.DELETE);
......
......@@ -708,20 +708,45 @@ declare module 'vscode' {
//#endregion
//#region mjbvz,joh: https://github.com/Microsoft/vscode/issues/43768
export interface FileCreateEvent {
readonly created: ReadonlyArray<Uri>;
}
export interface FileWillCreateEvent {
readonly creating: ReadonlyArray<Uri>;
waitUntil(thenable: Thenable<any>): void;
}
export interface FileDeleteEvent {
readonly deleted: ReadonlyArray<Uri>;
}
export interface FileWillDeleteEvent {
readonly deleting: ReadonlyArray<Uri>;
waitUntil(thenable: Thenable<any>): void;
}
export interface FileRenameEvent {
readonly oldUri: Uri;
readonly newUri: Uri;
readonly renamed: ReadonlyArray<{ oldUri: Uri, newUri: Uri }>;
}
export interface FileWillRenameEvent {
readonly oldUri: Uri;
readonly newUri: Uri;
readonly renaming: ReadonlyArray<{ oldUri: Uri, newUri: Uri }>;
waitUntil(thenable: Thenable<WorkspaceEdit>): void;
}
export namespace workspace {
export const onWillRenameFile: Event<FileWillRenameEvent>;
export const onDidRenameFile: Event<FileRenameEvent>;
export const onWillCreateFiles: Event<FileWillCreateEvent>;
export const onDidCreateFiles: Event<FileCreateEvent>;
export const onWillDeleteFiles: Event<FileWillDeleteEvent>;
export const onDidDeleteFiles: Event<FileDeleteEvent>;
export const onWillRenameFiles: Event<FileWillRenameEvent>;
export const onDidRenameFiles: Event<FileRenameEvent>;
}
//#endregion
......
......@@ -3,21 +3,24 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { DisposableStore } from 'vs/base/common/lifecycle';
import { FileChangeType, IFileService, FileOperation } from 'vs/platform/files/common/files';
import { extHostCustomer } from 'vs/workbench/api/common/extHostCustomers';
import { ExtHostContext, FileSystemEvents, IExtHostContext } from '../common/extHost.protocol';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress';
import { localize } from 'vs/nls';
@extHostCustomer
export class MainThreadFileSystemEventService {
private readonly _listener = new Array<IDisposable>();
private readonly _listener = new DisposableStore();
constructor(
extHostContext: IExtHostContext,
@IFileService fileService: IFileService,
@ITextFileService textfileService: ITextFileService,
@ITextFileService textFileService: ITextFileService,
@IProgressService progressService: IProgressService
) {
const proxy = extHostContext.getProxy(ExtHostContext.ExtHostFileSystemEventService);
......@@ -28,7 +31,7 @@ export class MainThreadFileSystemEventService {
changed: [],
deleted: []
};
fileService.onFileChanges(event => {
this._listener.add(fileService.onFileChanges(event => {
for (let change of event.changes) {
switch (change.type) {
case FileChangeType.ADDED:
......@@ -47,22 +50,35 @@ export class MainThreadFileSystemEventService {
events.created.length = 0;
events.changed.length = 0;
events.deleted.length = 0;
}, undefined, this._listener);
}));
// file operation events - (changes the editor makes)
fileService.onAfterOperation(e => {
if (e.isOperation(FileOperation.MOVE)) {
proxy.$onFileRename(e.resource, e.target.resource);
}
}, undefined, this._listener);
textfileService.onWillMove(e => {
const promise = proxy.$onWillRename(e.oldResource, e.newResource);
e.waitUntil(promise);
}, undefined, this._listener);
// BEFORE file operation
const messages = new Map<FileOperation, string>();
messages.set(FileOperation.CREATE, localize('msg-create', "Running 'File Create' participants..."));
messages.set(FileOperation.DELETE, localize('msg-delete', "Running 'File Delete' participants..."));
messages.set(FileOperation.MOVE, localize('msg-rename', "Running 'File Rename' participants..."));
this._listener.add(textFileService.onWillRunOperation(e => {
const p = progressService.withProgress({ location: ProgressLocation.Window }, progress => {
progress.report({ message: messages.get(e.operation) });
const p1 = proxy.$onWillRunFileOperation(e.operation, e.target, e.source);
const p2 = new Promise((_resolve, reject) => {
setTimeout(() => reject(new Error('timeout')), 5000);
});
return Promise.race([p1, p2]);
});
e.waitUntil(p);
}));
// AFTER file operation
this._listener.add(textFileService.onDidRunOperation(e => proxy.$onDidRunFileOperation(e.operation, e.target, e.source)));
}
dispose(): void {
dispose(this._listener);
this._listener.dispose();
}
}
......@@ -695,11 +695,27 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
checkProposedApiEnabled(extension);
return extHostLabelService.$registerResourceLabelFormatter(formatter);
},
onDidRenameFile: (listener: (e: vscode.FileRenameEvent) => any, thisArg?: any, disposables?: vscode.Disposable[]) => {
onDidCreateFiles: (listener, thisArg, disposables) => {
checkProposedApiEnabled(extension);
return extHostFileSystemEvent.onDidCreateFile(listener, thisArg, disposables);
},
onDidDeleteFiles: (listener, thisArg, disposables) => {
checkProposedApiEnabled(extension);
return extHostFileSystemEvent.onDidDeleteFile(listener, thisArg, disposables);
},
onDidRenameFiles: (listener, thisArg, disposables) => {
checkProposedApiEnabled(extension);
return extHostFileSystemEvent.onDidRenameFile(listener, thisArg, disposables);
},
onWillRenameFile: (listener: (e: vscode.FileWillRenameEvent) => any, thisArg?: any, disposables?: vscode.Disposable[]) => {
onWillCreateFiles: (listener: (e: vscode.FileWillCreateEvent) => any, thisArg?: any, disposables?: vscode.Disposable[]) => {
checkProposedApiEnabled(extension);
return extHostFileSystemEvent.getOnWillCreateFileEvent(extension)(listener, thisArg, disposables);
},
onWillDeleteFiles: (listener: (e: vscode.FileWillDeleteEvent) => any, thisArg?: any, disposables?: vscode.Disposable[]) => {
checkProposedApiEnabled(extension);
return extHostFileSystemEvent.getOnWillDeleteFileEvent(extension)(listener, thisArg, disposables);
},
onWillRenameFiles: (listener: (e: vscode.FileWillRenameEvent) => any, thisArg?: any, disposables?: vscode.Disposable[]) => {
checkProposedApiEnabled(extension);
return extHostFileSystemEvent.getOnWillRenameFileEvent(extension)(listener, thisArg, disposables);
}
......
......@@ -914,10 +914,11 @@ export interface FileSystemEvents {
changed: UriComponents[];
deleted: UriComponents[];
}
export interface ExtHostFileSystemEventServiceShape {
$onFileEvent(events: FileSystemEvents): void;
$onFileRename(oldUri: UriComponents, newUri: UriComponents): void;
$onWillRename(oldUri: UriComponents, newUri: UriComponents): Promise<any>;
$onWillRunFileOperation(operation: files.FileOperation, target: UriComponents, source: UriComponents | undefined): Promise<any>;
$onDidRunFileOperation(operation: files.FileOperation, target: UriComponents, source: UriComponents | undefined): void;
}
export interface ObjectIdentifier {
......
......@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { flatten } from 'vs/base/common/arrays';
import { AsyncEmitter, Emitter, Event } from 'vs/base/common/event';
import { AsyncEmitter, Emitter, Event, IWaitUntil } from 'vs/base/common/event';
import { IRelativePattern, parse } from 'vs/base/common/glob';
import { URI, UriComponents } from 'vs/base/common/uri';
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
......@@ -13,6 +13,7 @@ import { ExtHostFileSystemEventServiceShape, FileSystemEvents, IMainContext, Mai
import * as typeConverter from './extHostTypeConverters';
import { Disposable, WorkspaceEdit } from './extHostTypes';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { FileOperation } from 'vs/platform/files/common/files';
class FileSystemWatcher implements vscode.FileSystemWatcher {
......@@ -96,18 +97,26 @@ class FileSystemWatcher implements vscode.FileSystemWatcher {
}
}
interface WillRenameListener {
interface IExtensionListener<E> {
extension: IExtensionDescription;
(e: vscode.FileWillRenameEvent): any;
(e: E): any;
}
export class ExtHostFileSystemEventService implements ExtHostFileSystemEventServiceShape {
private readonly _onFileEvent = new Emitter<FileSystemEvents>();
private readonly _onFileSystemEvent = new Emitter<FileSystemEvents>();
private readonly _onDidRenameFile = new Emitter<vscode.FileRenameEvent>();
private readonly _onDidCreateFile = new Emitter<vscode.FileCreateEvent>();
private readonly _onDidDeleteFile = new Emitter<vscode.FileDeleteEvent>();
private readonly _onWillRenameFile = new AsyncEmitter<vscode.FileWillRenameEvent>();
private readonly _onWillCreateFile = new AsyncEmitter<vscode.FileWillCreateEvent>();
private readonly _onWillDeleteFile = new AsyncEmitter<vscode.FileWillDeleteEvent>();
readonly onDidRenameFile: Event<vscode.FileRenameEvent> = this._onDidRenameFile.event;
readonly onDidCreateFile: Event<vscode.FileCreateEvent> = this._onDidCreateFile.event;
readonly onDidDeleteFile: Event<vscode.FileDeleteEvent> = this._onDidDeleteFile.event;
constructor(
mainContext: IMainContext,
......@@ -117,37 +126,78 @@ export class ExtHostFileSystemEventService implements ExtHostFileSystemEventServ
//
}
public createFileSystemWatcher(globPattern: string | IRelativePattern, ignoreCreateEvents?: boolean, ignoreChangeEvents?: boolean, ignoreDeleteEvents?: boolean): vscode.FileSystemWatcher {
return new FileSystemWatcher(this._onFileEvent.event, globPattern, ignoreCreateEvents, ignoreChangeEvents, ignoreDeleteEvents);
//--- file events
createFileSystemWatcher(globPattern: string | IRelativePattern, ignoreCreateEvents?: boolean, ignoreChangeEvents?: boolean, ignoreDeleteEvents?: boolean): vscode.FileSystemWatcher {
return new FileSystemWatcher(this._onFileSystemEvent.event, globPattern, ignoreCreateEvents, ignoreChangeEvents, ignoreDeleteEvents);
}
$onFileEvent(events: FileSystemEvents) {
this._onFileEvent.fire(events);
this._onFileSystemEvent.fire(events);
}
$onFileRename(oldUri: UriComponents, newUri: UriComponents) {
this._onDidRenameFile.fire(Object.freeze({ oldUri: URI.revive(oldUri), newUri: URI.revive(newUri) }));
//--- file operations
$onDidRunFileOperation(operation: FileOperation, target: UriComponents, source: UriComponents | undefined): void {
switch (operation) {
case FileOperation.MOVE:
this._onDidRenameFile.fire(Object.freeze({ renamed: [{ oldUri: URI.revive(target), newUri: URI.revive(source!) }] }));
break;
case FileOperation.DELETE:
this._onDidDeleteFile.fire(Object.freeze({ deleted: [URI.revive(target)] }));
break;
case FileOperation.CREATE:
this._onDidCreateFile.fire(Object.freeze({ created: [URI.revive(target)] }));
break;
default:
//ignore, dont send
}
}
getOnWillRenameFileEvent(extension: IExtensionDescription): Event<vscode.FileWillRenameEvent> {
return this._createWillExecuteEvent(extension, this._onWillRenameFile);
}
getOnWillCreateFileEvent(extension: IExtensionDescription): Event<vscode.FileWillCreateEvent> {
return this._createWillExecuteEvent(extension, this._onWillCreateFile);
}
getOnWillDeleteFileEvent(extension: IExtensionDescription): Event<vscode.FileWillDeleteEvent> {
return this._createWillExecuteEvent(extension, this._onWillDeleteFile);
}
private _createWillExecuteEvent<E extends IWaitUntil>(extension: IExtensionDescription, emitter: AsyncEmitter<E>): Event<E> {
return (listener, thisArg, disposables) => {
const wrappedListener: WillRenameListener = <any>((e: vscode.FileWillRenameEvent) => {
listener.call(thisArg, e);
});
const wrappedListener: IExtensionListener<E> = function wrapped(e: E) { listener.call(thisArg, e); };
wrappedListener.extension = extension;
return this._onWillRenameFile.event(wrappedListener, undefined, disposables);
return emitter.event(wrappedListener, undefined, disposables);
};
}
$onWillRename(oldUriDto: UriComponents, newUriDto: UriComponents): Promise<any> {
const oldUri = URI.revive(oldUriDto);
const newUri = URI.revive(newUriDto);
async $onWillRunFileOperation(operation: FileOperation, target: UriComponents, source: UriComponents | undefined): Promise<any> {
switch (operation) {
case FileOperation.MOVE:
await this._fireWillRename(URI.revive(target), URI.revive(source!));
break;
case FileOperation.DELETE:
this._onWillDeleteFile.fireAsync(thenables => (<vscode.FileWillDeleteEvent>{ deleting: [URI.revive(target)], waitUntil: p => thenables.push(Promise.resolve(p)) }));
break;
case FileOperation.CREATE:
this._onWillCreateFile.fireAsync(thenables => (<vscode.FileWillCreateEvent>{ creating: [URI.revive(target)], waitUntil: p => thenables.push(Promise.resolve(p)) }));
break;
default:
//ignore, dont send
}
}
private async _fireWillRename(oldUri: URI, newUri: URI): Promise<any> {
const edits: WorkspaceEdit[] = [];
return Promise.resolve(this._onWillRenameFile.fireAsync((bucket, _listener) => {
await Promise.resolve(this._onWillRenameFile.fireAsync(bucket => {
return {
oldUri,
newUri,
renaming: [{ oldUri, newUri }],
waitUntil: (thenable: Promise<vscode.WorkspaceEdit>): void => {
if (Object.isFrozen(bucket)) {
throw new TypeError('waitUntil cannot be called async');
......@@ -163,10 +213,12 @@ export class ExtHostFileSystemEventService implements ExtHostFileSystemEventServ
bucket.push(wrappedThenable);
}
};
}).then((): any => {
}));
if (edits.length === 0) {
return undefined;
}
// flatten all WorkspaceEdits collected via waitUntil-call
// and apply them in one go.
const allEdits = new Array<Array<IResourceFileEditDto | IResourceTextEditDto>>();
......@@ -177,6 +229,7 @@ export class ExtHostFileSystemEventService implements ExtHostFileSystemEventServ
}
}
return this._mainThreadTextEditors.$tryApplyWorkspaceEdit({ edits: flatten(allEdits) });
}));
}
}
......@@ -5,16 +5,15 @@
import * as nls from 'vs/nls';
import { URI } from 'vs/base/common/uri';
import * as errors from 'vs/base/common/errors';
import * as objects from 'vs/base/common/objects';
import { Event, Emitter } from 'vs/base/common/event';
import { Event, Emitter, AsyncEmitter } from 'vs/base/common/event';
import * as platform from 'vs/base/common/platform';
import { IBackupFileService } from 'vs/workbench/services/backup/common/backup';
import { IResult, ITextFileOperationResult, ITextFileService, ITextFileStreamContent, IAutoSaveConfiguration, AutoSaveMode, SaveReason, ITextFileEditorModelManager, ITextFileEditorModel, ModelState, ISaveOptions, AutoSaveContext, IWillMoveEvent, ITextFileContent, IResourceEncodings, IReadTextFileOptions, IWriteTextFileOptions, toBufferOrReadable, TextFileOperationError, TextFileOperationResult } from 'vs/workbench/services/textfile/common/textfiles';
import { IResult, ITextFileOperationResult, ITextFileService, ITextFileStreamContent, IAutoSaveConfiguration, AutoSaveMode, SaveReason, ITextFileEditorModelManager, ITextFileEditorModel, ModelState, ISaveOptions, AutoSaveContext, ITextFileContent, IResourceEncodings, IReadTextFileOptions, IWriteTextFileOptions, toBufferOrReadable, TextFileOperationError, TextFileOperationResult, FileOperationWillRunEvent, FileOperationDidRunEvent } from 'vs/workbench/services/textfile/common/textfiles';
import { ConfirmResult, IRevertOptions } from 'vs/workbench/common/editor';
import { ILifecycleService, ShutdownReason, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
import { IFileService, IFilesConfiguration, FileOperationError, FileOperationResult, AutoSaveConfiguration, HotExitConfiguration, IFileStatWithMetadata, ICreateFileOptions } from 'vs/platform/files/common/files';
import { IFileService, IFilesConfiguration, FileOperationError, FileOperationResult, AutoSaveConfiguration, HotExitConfiguration, IFileStatWithMetadata, ICreateFileOptions, FileOperation } from 'vs/platform/files/common/files';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { Disposable } from 'vs/base/common/lifecycle';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
......@@ -53,8 +52,13 @@ export abstract class AbstractTextFileService extends Disposable implements ITex
private readonly _onFilesAssociationChange: Emitter<void> = this._register(new Emitter<void>());
readonly onFilesAssociationChange: Event<void> = this._onFilesAssociationChange.event;
private readonly _onWillMove = this._register(new Emitter<IWillMoveEvent>());
readonly onWillMove: Event<IWillMoveEvent> = this._onWillMove.event;
private _onWillRunOperation = this._register(new AsyncEmitter<FileOperationWillRunEvent>());
readonly onWillRunOperation = this._onWillRunOperation.event;
private _onDidRunOperation = this._register(new Emitter<FileOperationDidRunEvent>());
readonly onDidRunOperation = this._onDidRunOperation.event;
private _models: TextFileEditorModelManager;
get models(): ITextFileEditorModelManager { return this._models; }
......@@ -409,6 +413,10 @@ export abstract class AbstractTextFileService extends Disposable implements ITex
}
async create(resource: URI, value?: string | ITextSnapshot, options?: ICreateFileOptions): Promise<IFileStatWithMetadata> {
// before event
await this._onWillRunOperation.fireAsync(promises => new FileOperationWillRunEvent(promises, FileOperation.CREATE, resource));
const stat = await this.doCreate(resource, value, options);
// If we had an existing model for the given resource, load
......@@ -420,6 +428,9 @@ export abstract class AbstractTextFileService extends Disposable implements ITex
await existingModel.revert();
}
// after event
this._onDidRunOperation.fire(new FileOperationDidRunEvent(FileOperation.CREATE, resource));
return stat;
}
......@@ -432,17 +443,20 @@ export abstract class AbstractTextFileService extends Disposable implements ITex
}
async delete(resource: URI, options?: { useTrash?: boolean, recursive?: boolean }): Promise<void> {
const dirtyFiles = this.getDirty().filter(dirty => isEqualOrParent(dirty, resource));
await this._onWillRunOperation.fireAsync(promises => new FileOperationWillRunEvent(promises, FileOperation.DELETE, resource));
const dirtyFiles = this.getDirty().filter(dirty => isEqualOrParent(dirty, resource));
await this.revertAll(dirtyFiles, { soft: true });
return this.fileService.del(resource, options);
await this.fileService.del(resource, options);
this._onDidRunOperation.fire(new FileOperationDidRunEvent(FileOperation.DELETE, resource));
}
async move(source: URI, target: URI, overwrite?: boolean): Promise<IFileStatWithMetadata> {
// await onWillMove event joiners
await this.notifyOnWillMove(source, target);
// before events
await this._onWillRunOperation.fireAsync(promises => new FileOperationWillRunEvent(promises, FileOperation.MOVE, target, source));
// find all models that related to either source or target (can be many if resource is a folder)
const sourceModels: ITextFileEditorModel[] = [];
......@@ -521,25 +535,10 @@ export abstract class AbstractTextFileService extends Disposable implements ITex
}
}));
return stat;
}
private async notifyOnWillMove(source: URI, target: URI): Promise<void> {
const waitForPromises: Promise<unknown>[] = [];
// fire event
this._onWillMove.fire({
oldResource: source,
newResource: target,
waitUntil(promise: Promise<unknown>) {
waitForPromises.push(promise.then(undefined, errors.onUnexpectedError));
}
});
// after event
this._onDidRunOperation.fire(new FileOperationDidRunEvent(FileOperation.MOVE, target, source));
// prevent async waitUntil-calls
Object.freeze(waitForPromises);
await Promise.all(waitForPromises);
return stat;
}
//#endregion
......
......@@ -4,10 +4,10 @@
*--------------------------------------------------------------------------------------------*/
import { URI } from 'vs/base/common/uri';
import { Event } from 'vs/base/common/event';
import { Event, IWaitUntil } from 'vs/base/common/event';
import { IDisposable } from 'vs/base/common/lifecycle';
import { IEncodingSupport, ConfirmResult, IRevertOptions, IModeSupport } from 'vs/workbench/common/editor';
import { IBaseStatWithMetadata, IFileStatWithMetadata, IReadFileOptions, IWriteFileOptions, FileOperationError, FileOperationResult } from 'vs/platform/files/common/files';
import { IBaseStatWithMetadata, IFileStatWithMetadata, IReadFileOptions, IWriteFileOptions, FileOperationError, FileOperationResult, FileOperation } from 'vs/platform/files/common/files';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { ITextEditorModel } from 'vs/editor/common/services/resolverService';
import { ITextBufferFactory, ITextModel, ITextSnapshot } from 'vs/editor/common/model';
......@@ -22,14 +22,22 @@ export interface ITextFileService extends IDisposable {
_serviceBrand: undefined;
readonly onWillMove: Event<IWillMoveEvent>;
readonly onAutoSaveConfigurationChange: Event<IAutoSaveConfiguration>;
readonly onFilesAssociationChange: Event<void>;
readonly isHotExitEnabled: boolean;
/**
* An event that is fired before attempting a certain file operation.
*/
readonly onWillRunOperation: Event<FileOperationWillRunEvent>;
/**
* An event that is fired after a file operation has been performed.
*/
readonly onDidRunOperation: Event<FileOperationDidRunEvent>;
/**
* Access to the manager of text file editor models providing further methods to work with them.
*/
......@@ -147,6 +155,35 @@ export interface ITextFileService extends IDisposable {
getAutoSaveConfiguration(): IAutoSaveConfiguration;
}
export class FileOperationWillRunEvent implements IWaitUntil {
constructor(
private _thenables: Promise<any>[],
readonly operation: FileOperation,
readonly target: URI,
readonly source?: URI | undefined
) { }
waitUntil(thenable: Promise<any>): void {
if (Object.isFrozen(this._thenables)) {
throw new Error('waitUntil cannot be used aync');
}
this._thenables.push(thenable);
}
}
export class FileOperationDidRunEvent {
constructor(
readonly operation: FileOperation,
readonly target: URI,
readonly source?: URI | undefined
) { }
}
export interface IReadTextFileOptions extends IReadFileOptions {
/**
......@@ -492,13 +529,6 @@ export interface IResolvedTextFileEditorModel extends ITextFileEditorModel {
createSnapshot(): ITextSnapshot;
}
export interface IWillMoveEvent {
oldResource: URI;
newResource: URI;
waitUntil(p: Promise<unknown>): void;
}
/**
* Helper method to convert a snapshot into its full string form.
*/
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册