提交 80fffd9c 编写于 作者: J Johannes Rieken

add onWillRunOperation-event to file service

上级 4b7e7891
......@@ -4,9 +4,9 @@
*--------------------------------------------------------------------------------------------*/
import { Disposable, IDisposable, toDisposable, dispose, DisposableStore } from 'vs/base/common/lifecycle';
import { IFileService, IResolveFileOptions, FileChangesEvent, FileOperationEvent, IFileSystemProviderRegistrationEvent, IFileSystemProvider, IFileStat, IResolveFileResult, ICreateFileOptions, IFileSystemProviderActivationEvent, FileOperationError, FileOperationResult, FileOperation, FileSystemProviderCapabilities, FileType, toFileSystemProviderErrorCode, FileSystemProviderErrorCode, IStat, IFileStatWithMetadata, IResolveMetadataFileOptions, etag, hasReadWriteCapability, hasFileFolderCopyCapability, hasOpenReadWriteCloseCapability, toFileOperationResult, IFileSystemProviderWithOpenReadWriteCloseCapability, IFileSystemProviderWithFileReadWriteCapability, IResolveFileResultWithMetadata, IWatchOptions, IWriteFileOptions, IReadFileOptions, IFileStreamContent, IFileContent, ETAG_DISABLED } from 'vs/platform/files/common/files';
import { IFileService, IResolveFileOptions, FileChangesEvent, FileOperationEvent, IFileSystemProviderRegistrationEvent, IFileSystemProvider, IFileStat, IResolveFileResult, ICreateFileOptions, IFileSystemProviderActivationEvent, FileOperationError, FileOperationResult, FileOperation, FileSystemProviderCapabilities, FileType, toFileSystemProviderErrorCode, FileSystemProviderErrorCode, IStat, IFileStatWithMetadata, IResolveMetadataFileOptions, etag, hasReadWriteCapability, hasFileFolderCopyCapability, hasOpenReadWriteCloseCapability, toFileOperationResult, IFileSystemProviderWithOpenReadWriteCloseCapability, IFileSystemProviderWithFileReadWriteCapability, IResolveFileResultWithMetadata, IWatchOptions, IWriteFileOptions, IReadFileOptions, IFileStreamContent, IFileContent, ETAG_DISABLED, FileOperationWillRunEvent } from 'vs/platform/files/common/files';
import { URI } from 'vs/base/common/uri';
import { Event, Emitter } from 'vs/base/common/event';
import { Event, Emitter, AsyncEmitter } from 'vs/base/common/event';
import { isAbsolutePath, dirname, basename, joinPath, isEqual, isEqualOrParent } from 'vs/base/common/resources';
import { localize } from 'vs/nls';
import { TernarySearchTree } from 'vs/base/common/map';
......@@ -131,6 +131,9 @@ export class FileService extends Disposable implements IFileService {
//#endregion
private _onWillRunOperation = this._register(new AsyncEmitter<FileOperationWillRunEvent>());
readonly onWillRunOperation = this._onWillRunOperation.event;
private _onAfterOperation: Emitter<FileOperationEvent> = this._register(new Emitter<FileOperationEvent>());
readonly onAfterOperation: Event<FileOperationEvent> = this._onAfterOperation.event;
......@@ -279,6 +282,9 @@ export class FileService extends Disposable implements IFileService {
throw new FileOperationError(localize('fileExists', "File to create already exists ({0})", this.resourceForError(resource)), FileOperationResult.FILE_MODIFIED_SINCE, options);
}
// before events
await this._onWillRunOperation.fireAsync(promises => new FileOperationWillRunEvent(promises, FileOperation.CREATE, resource));
// do write into file (this will create it too)
const fileStat = await this.writeFile(resource, bufferOrReadableOrStream);
......@@ -542,6 +548,9 @@ export class FileService extends Disposable implements IFileService {
const sourceProvider = this.throwIfFileSystemIsReadonly(await this.withReadWriteProvider(source));
const targetProvider = this.throwIfFileSystemIsReadonly(await this.withReadWriteProvider(target));
// before events
await this._onWillRunOperation.fireAsync(promises => new FileOperationWillRunEvent(promises, FileOperation.MOVE, target, source));
// move
const mode = await this.doMoveCopy(sourceProvider, source, targetProvider, target, 'move', !!overwrite);
......@@ -556,6 +565,9 @@ export class FileService extends Disposable implements IFileService {
const sourceProvider = await this.withReadWriteProvider(source);
const targetProvider = this.throwIfFileSystemIsReadonly(await this.withReadWriteProvider(target));
// before events
await this._onWillRunOperation.fireAsync(promises => new FileOperationWillRunEvent(promises, FileOperation.COPY, target, source));
// copy
const mode = await this.doMoveCopy(sourceProvider, source, targetProvider, target, 'copy', !!overwrite);
......@@ -710,6 +722,9 @@ export class FileService extends Disposable implements IFileService {
async createFolder(resource: URI): Promise<IFileStatWithMetadata> {
const provider = this.throwIfFileSystemIsReadonly(await this.withProvider(resource));
// before events
await this._onWillRunOperation.fireAsync(promises => new FileOperationWillRunEvent(promises, FileOperation.CREATE, resource));
// mkdir recursively
await this.mkdirp(provider, resource);
......@@ -772,6 +787,9 @@ export class FileService extends Disposable implements IFileService {
}
}
// Before Event
await this._onWillRunOperation.fireAsync(promises => new FileOperationWillRunEvent(promises, FileOperation.DELETE, resource));
// Delete through provider
await provider.delete(resource, { recursive, useTrash });
......
......@@ -7,7 +7,7 @@ import { sep } from 'vs/base/common/path';
import { URI } from 'vs/base/common/uri';
import * as glob from 'vs/base/common/glob';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { Event } from 'vs/base/common/event';
import { Event, IWaitUntil } from 'vs/base/common/event';
import { startsWithIgnoreCase } from 'vs/base/common/strings';
import { IDisposable } from 'vs/base/common/lifecycle';
import { isEqualOrParent, isEqual } from 'vs/base/common/resources';
......@@ -57,6 +57,11 @@ export interface IFileService {
*/
readonly onFileChanges: Event<FileChangesEvent>;
/**
* An event that is fired before attempting a certain file operation.
*/
readonly onWillRunOperation: Event<FileOperationWillRunEvent>;
/**
* An event that is fired upon successful completion of a certain file operation.
*/
......@@ -372,6 +377,23 @@ export class FileOperationEvent {
}
}
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);
}
}
/**
* Possible changes that can occur to a file.
*/
......
......@@ -15,7 +15,7 @@ import { getPathFromAmdModule } from 'vs/base/common/amd';
import { copy, rimraf, symlink, RimRafMode, rimrafSync } from 'vs/base/node/pfs';
import { URI } from 'vs/base/common/uri';
import { existsSync, statSync, readdirSync, readFileSync, writeFileSync, renameSync, unlinkSync, mkdirSync, createReadStream } from 'fs';
import { FileOperation, FileOperationEvent, IFileStat, FileOperationResult, FileSystemProviderCapabilities, FileChangeType, IFileChange, FileChangesEvent, FileOperationError, etag, IStat, IFileStatWithMetadata } from 'vs/platform/files/common/files';
import { FileOperation, FileOperationEvent, IFileStat, FileOperationResult, FileSystemProviderCapabilities, FileChangeType, IFileChange, FileChangesEvent, FileOperationError, etag, IStat, IFileStatWithMetadata, FileOperationWillRunEvent } from 'vs/platform/files/common/files';
import { NullLogService } from 'vs/platform/log/common/log';
import { isLinux, isWindows } from 'vs/base/common/platform';
import { DisposableStore } from 'vs/base/common/lifecycle';
......@@ -155,6 +155,9 @@ suite('Disk File Service', function () {
});
test('createFolder', async () => {
let beforeEvent: FileOperationWillRunEvent | undefined;
disposables.add(service.onWillRunOperation(e => beforeEvent = e));
let event: FileOperationEvent | undefined;
disposables.add(service.onAfterOperation(e => event = e));
......@@ -167,6 +170,11 @@ suite('Disk File Service', function () {
assert.equal(newFolder.name, 'newFolder');
assert.equal(existsSync(newFolder.resource.fsPath), true);
assert.ok(beforeEvent);
assert.equal(beforeEvent!.target.fsPath, newFolderResource.fsPath);
assert.equal(beforeEvent!.source, undefined);
assert.equal(beforeEvent!.operation, FileOperation.CREATE);
assert.ok(event);
assert.equal(event!.resource.fsPath, newFolderResource.fsPath);
assert.equal(event!.operation, FileOperation.CREATE);
......@@ -410,6 +418,9 @@ suite('Disk File Service', function () {
});
test('deleteFile', async () => {
let beforeEvent: FileOperationWillRunEvent | undefined;
disposables.add(service.onWillRunOperation(e => beforeEvent = e));
let event: FileOperationEvent;
disposables.add(service.onAfterOperation(e => event = e));
......@@ -419,6 +430,12 @@ suite('Disk File Service', function () {
await service.del(source.resource);
assert.equal(existsSync(source.resource.fsPath), false);
assert.ok(beforeEvent!);
assert.equal(beforeEvent!.target.fsPath, resource.fsPath);
assert.equal(beforeEvent!.source, undefined);
assert.equal(beforeEvent!.operation, FileOperation.DELETE);
assert.ok(event!);
assert.equal(event!.resource.fsPath, resource.fsPath);
assert.equal(event!.operation, FileOperation.DELETE);
......@@ -454,6 +471,9 @@ suite('Disk File Service', function () {
});
test('move', async () => {
let beforeEvent: FileOperationWillRunEvent | undefined;
disposables.add(service.onWillRunOperation(e => beforeEvent = e));
let event: FileOperationEvent;
disposables.add(service.onAfterOperation(e => event = e));
......@@ -466,6 +486,10 @@ suite('Disk File Service', function () {
assert.equal(existsSync(renamed.resource.fsPath), true);
assert.equal(existsSync(source.fsPath), false);
assert.ok(beforeEvent!);
assert.equal(beforeEvent!.operation, FileOperation.MOVE);
assert.equal(beforeEvent!.source?.fsPath, source.fsPath);
assert.equal(beforeEvent!.target?.fsPath, target.fsPath);
assert.ok(event!);
assert.equal(event!.resource.fsPath, source.fsPath);
assert.equal(event!.operation, FileOperation.MOVE);
......@@ -820,6 +844,9 @@ suite('Disk File Service', function () {
}
async function doTestCopy(sourceName: string = 'index.html') {
let beforeEvent: FileOperationWillRunEvent | undefined;
disposables.add(service.onWillRunOperation(e => beforeEvent = e));
let event: FileOperationEvent;
disposables.add(service.onAfterOperation(e => event = e));
......@@ -830,6 +857,10 @@ suite('Disk File Service', function () {
assert.equal(existsSync(copied.resource.fsPath), true);
assert.equal(existsSync(source.resource.fsPath), true);
assert.ok(beforeEvent!);
assert.equal(beforeEvent!.source!.fsPath, source.resource.fsPath);
assert.equal(beforeEvent!.operation, FileOperation.COPY);
assert.equal(beforeEvent!.target!.fsPath, copied.resource.fsPath);
assert.ok(event!);
assert.equal(event!.resource.fsPath, source.resource.fsPath);
assert.equal(event!.operation, FileOperation.COPY);
......@@ -1338,6 +1369,9 @@ suite('Disk File Service', function () {
});
async function assertCreateFile(converter: (content: string) => VSBuffer | VSBufferReadable | VSBufferReadableStream): Promise<void> {
let beforeEvent: FileOperationWillRunEvent | undefined;
disposables.add(service.onWillRunOperation(e => beforeEvent = e));
let event: FileOperationEvent;
disposables.add(service.onAfterOperation(e => event = e));
......@@ -1348,6 +1382,10 @@ suite('Disk File Service', function () {
assert.equal(existsSync(fileStat.resource.fsPath), true);
assert.equal(readFileSync(fileStat.resource.fsPath), contents);
assert.ok(beforeEvent!);
assert.equal(beforeEvent!.target.fsPath, resource.fsPath);
assert.equal(beforeEvent!.source, undefined);
assert.equal(beforeEvent!.operation, FileOperation.CREATE);
assert.ok(event!);
assert.equal(event!.resource.fsPath, resource.fsPath);
assert.equal(event!.operation, FileOperation.CREATE);
......
......@@ -923,6 +923,8 @@ export class TestFileService implements IFileService {
public _serviceBrand: undefined;
readonly onWillRunOperation = Event.None;
private readonly _onFileChanges: Emitter<FileChangesEvent>;
private readonly _onAfterOperation: Emitter<FileOperationEvent>;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册