diff --git a/src/vs/platform/files/common/files.ts b/src/vs/platform/files/common/files.ts index fa1a6aa28b1..c99d1c471d1 100644 --- a/src/vs/platform/files/common/files.ts +++ b/src/vs/platform/files/common/files.ts @@ -159,27 +159,9 @@ export interface FileOverwriteOptions { overwrite: boolean; } -export interface FileOptions { - /** - * Create a file when it doesn't exists. - */ - create?: boolean; - - /** - * In combination with [`create`](FileOptions.create) but - * the operation should fail when a file already exists. - */ - exclusive?: boolean; - - /** - * Open a file for reading. - */ - read?: boolean; - - /** - * Open a file for writing. - */ - write?: boolean; +export interface FileWriteOptions { + overwrite: boolean; + create: boolean; } export enum FileType { @@ -224,10 +206,10 @@ export interface IFileSystemProvider { rename(from: URI, to: URI, opts: FileOverwriteOptions): TPromise; copy?(from: URI, to: URI, opts: FileOverwriteOptions): TPromise; - readFile?(resource: URI, opts: FileOptions): TPromise; - writeFile?(resource: URI, content: Uint8Array, opts: FileOptions): TPromise; + readFile?(resource: URI): TPromise; + writeFile?(resource: URI, content: Uint8Array, opts: FileWriteOptions): TPromise; - open?(resource: URI, opts: FileOptions): TPromise; + open?(resource: URI): TPromise; close?(fd: number): TPromise; read?(fd: number, pos: number, data: Uint8Array, offset: number, length: number): TPromise; write?(fd: number, pos: number, data: Uint8Array, offset: number, length: number): TPromise; diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index cfc2483856f..9f1239e78f2 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -4997,58 +4997,69 @@ declare module 'vscode' { /** * Subscribe to events in the file or folder denoted by `uri`. - * @param uri - * @param options + * @param uri The uri of the file to be watched. + * @param options Configures the watch. + * @returns A disposable that tells the provider to stop watching this `uri`. */ watch(uri: Uri, options: { recursive: boolean; excludes: string[] }): Disposable; /** - * Retrieve metadata about a file. Throw an [`FileNotFound`](#FileSystemError.FileNotFound)-error - * in case the file does not exist. + * Retrieve metadata about a file. * * @param uri The uri of the file to retrieve meta data about. * @return The file metadata about the file. + * @throws [`FileNotFound`](#FileSystemError.FileNotFound) when `uri` doesn't exist. */ - stat(uri: Uri, options: { /*future: followSymlinks*/ }): FileStat | Thenable; + stat(uri: Uri): FileStat | Thenable; /** - * Retrieve the meta data of all entries of a [directory](#FileStat.isDirectory) + * Retrieve the meta data of all entries of a [directory](#FileType.Directory) * * @param uri The uri of the folder. - * @return A thenable that resolves to an array of tuples of file names and files stats. + * @return An array of name/type-tuples or a thenable that resolves to such. + * @throws [`FileNotFound`](#FileSystemError.FileNotFound) when `uri` doesn't exist. */ - readDirectory(uri: Uri, options: { /*future: onlyType?*/ }): [string, FileType][] | Thenable<[string, FileType][]>; + readDirectory(uri: Uri): [string, FileType][] | Thenable<[string, FileType][]>; /** * Create a new directory. *Note* that new files are created via `write`-calls. * - * @param uri The uri of the *new* folder. + * @param uri The uri of the new folder. + * @returns Metadata about the created directory or a thenable that resolves to such. + * @throws [`FileNotFound`](#FileSystemError.FileNotFound) when the parent of `uri` doesn't exist. + * @throws [`FileExists`](#FileSystemError.FileExists) when `uri` already exists. */ - createDirectory(uri: Uri, options: { /*future: permissions?*/ }): FileStat | Thenable; + createDirectory(uri: Uri): FileStat | Thenable; /** * Read the entire contents of a file. * * @param uri The uri of the file. - * @return A thenable that resolves to an array of bytes. + * @return An array of bytes or a thenable that resolves to such. + * @throws [`FileNotFound`](#FileSystemError.FileNotFound) when `uri` doesn't exist. */ - readFile(uri: Uri, options: FileOptions): Uint8Array | Thenable; + readFile(uri: Uri): Uint8Array | Thenable; /** * Write data to a file, replacing its entire contents. * * @param uri The uri of the file. * @param content The new content of the file. + * @param options Defines is missing files should or must be created. + * @throws [`FileNotFound`](#FileSystemError.FileNotFound) when `uri` doesn't exist and `create` is not set. + * @throws [`FileNotFound`](#FileSystemError.FileNotFound) when the parent of `uri` doesn't exist and `create` is set. + * @throws [`FileExists`](#FileSystemError.FileExists) when `uri` already exists and `overwrite` is set. */ - writeFile(uri: Uri, content: Uint8Array, options: FileOptions): void | Thenable; + writeFile(uri: Uri, content: Uint8Array, options: { create: boolean, overwrite: boolean }): void | Thenable; /** * Delete a file. * - * @param uri The resource that is to be deleted - * @param options Options bag for future use + * @param uri The resource that is to be deleted. + * @param options Defines if deletion of folders is recursive. + * @throws [`FileNotFound`](#FileSystemError.FileNotFound) when `uri` doesn't exist. */ - delete(uri: Uri, options: { /*future: useTrash?, followSymlinks?*/ }): void | Thenable; + delete(uri: Uri, options: { recursive: boolean }): void | Thenable; /** * Rename a file or folder. @@ -5056,8 +5067,9 @@ declare module 'vscode' { * @param oldUri The existing file or folder. * @param newUri The target location. * @param options Defines if existing files should be overwriten. - * @throws [`FileNotFound`](FileSystemError.FileNotFound) when `oldUri` doesn't exist - * @throws [`FileExists`](FileSystemError.FileExists) when `newUri` exists and when the `overwrite` option is not `true`. + * @returns Metadata about the renamed file or a thenable that resolves to such. + * @throws [`FileNotFound`](#FileSystemError.FileNotFound) when `oldUri` doesn't exist. + * @throws [`FileExists`](#FileSystemError.FileExists) when `newUri` exists and when the `overwrite` option is not `true`. */ rename(oldUri: Uri, newUri: Uri, options: { overwrite: boolean }): FileStat | Thenable; @@ -5068,6 +5080,7 @@ declare module 'vscode' { * @param source The existing file or folder. * @param destination The destination location. * @param options Defines if existing files should be overwriten. + * @returns Metadata about the copied file or a thenable that resolves to such. * @throws [`FileNotFound`](FileSystemError.FileNotFound) when `source` doesn't exist * @throws [`FileExists`](FileSystemError.FileExists) when `destination` exists and when the `overwrite` option is not `true`. */ diff --git a/src/vs/workbench/api/electron-browser/mainThreadFileSystem.ts b/src/vs/workbench/api/electron-browser/mainThreadFileSystem.ts index 7daef885dc1..ed8c02f6975 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadFileSystem.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadFileSystem.ts @@ -8,7 +8,7 @@ import { Emitter, Event } from 'vs/base/common/event'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import URI from 'vs/base/common/uri'; import { TPromise } from 'vs/base/common/winjs.base'; -import { FileOptions, FileSystemProviderCapabilities, IFileChange, IFileService, IFileSystemProvider, IStat, IWatchOptions, FileType, FileOverwriteOptions } from 'vs/platform/files/common/files'; +import { FileWriteOptions, FileSystemProviderCapabilities, IFileChange, IFileService, IFileSystemProvider, IStat, IWatchOptions, FileType, FileOverwriteOptions } from 'vs/platform/files/common/files'; import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers'; import { ExtHostContext, ExtHostFileSystemShape, IExtHostContext, IFileChangeDto, MainContext, MainThreadFileSystemShape } from '../node/extHost.protocol'; @@ -94,13 +94,13 @@ class RemoteFileSystemProvider implements IFileSystemProvider { }); } - readFile(resource: URI, opts: FileOptions): TPromise { - return this._proxy.$readFile(this._handle, resource, opts).then(encoded => { + readFile(resource: URI): TPromise { + return this._proxy.$readFile(this._handle, resource).then(encoded => { return Buffer.from(encoded, 'base64'); }); } - writeFile(resource: URI, content: Uint8Array, opts: FileOptions): TPromise { + writeFile(resource: URI, content: Uint8Array, opts: FileWriteOptions): TPromise { let encoded = Buffer.isBuffer(content) ? content.toString('base64') : Buffer.from(content.buffer, content.byteOffset, content.byteLength).toString('base64'); diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index ac488b829cc..8a7ee4a5de7 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -42,7 +42,7 @@ import { ITreeItem } from 'vs/workbench/common/views'; import { ThemeColor } from 'vs/platform/theme/common/themeService'; import { IDisposable } from 'vs/base/common/lifecycle'; import { SerializedError } from 'vs/base/common/errors'; -import { IStat, FileChangeType, IWatchOptions, FileSystemProviderCapabilities, FileOptions, FileType, FileOverwriteOptions } from 'vs/platform/files/common/files'; +import { IStat, FileChangeType, IWatchOptions, FileSystemProviderCapabilities, FileWriteOptions, FileType, FileOverwriteOptions } from 'vs/platform/files/common/files'; import { ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; import { CommentRule, CharacterPair, EnterAction } from 'vs/editor/common/modes/languageConfiguration'; import { ISingleEditOperation } from 'vs/editor/common/model'; @@ -583,8 +583,8 @@ export interface ExtHostWorkspaceShape { export interface ExtHostFileSystemShape { $stat(handle: number, resource: UriComponents): TPromise; $readdir(handle: number, resource: UriComponents): TPromise<[string, FileType][]>; - $readFile(handle: number, resource: UriComponents, opts: FileOptions): TPromise; - $writeFile(handle: number, resource: UriComponents, base64Encoded: string, opts: FileOptions): TPromise; + $readFile(handle: number, resource: UriComponents): TPromise; + $writeFile(handle: number, resource: UriComponents, base64Encoded: string, opts: FileWriteOptions): TPromise; $rename(handle: number, resource: UriComponents, target: UriComponents, opts: FileOverwriteOptions): TPromise; $copy(handle: number, resource: UriComponents, target: UriComponents, opts: FileOverwriteOptions): TPromise; $mkdir(handle: number, resource: UriComponents): TPromise; diff --git a/src/vs/workbench/api/node/extHostFileSystem.ts b/src/vs/workbench/api/node/extHostFileSystem.ts index 954ce34c00b..f46c802aeaa 100644 --- a/src/vs/workbench/api/node/extHostFileSystem.ts +++ b/src/vs/workbench/api/node/extHostFileSystem.ts @@ -152,7 +152,7 @@ class FileSystemProviderShim implements vscode.FileSystemProvider { }); } - writeFile(resource: vscode.Uri, content: Uint8Array, options: files.FileOptions): Thenable { + writeFile(resource: vscode.Uri, content: Uint8Array, options: files.FileWriteOptions): Thenable { return this._delegate.write(resource, content); } } @@ -249,27 +249,27 @@ export class ExtHostFileSystem implements ExtHostFileSystemShape { } $stat(handle: number, resource: UriComponents): TPromise { - return asWinJsPromise(token => this._fsProvider.get(handle).stat(URI.revive(resource), {})).then(ExtHostFileSystem._asIStat); + return asWinJsPromise(token => this._fsProvider.get(handle).stat(URI.revive(resource))).then(ExtHostFileSystem._asIStat); } $readdir(handle: number, resource: UriComponents): TPromise<[string, files.FileType][], any> { - return asWinJsPromise(token => this._fsProvider.get(handle).readDirectory(URI.revive(resource), {})); + return asWinJsPromise(token => this._fsProvider.get(handle).readDirectory(URI.revive(resource))); } - $readFile(handle: number, resource: UriComponents, opts: files.FileOptions): TPromise { + $readFile(handle: number, resource: UriComponents): TPromise { return asWinJsPromise(token => { - return this._fsProvider.get(handle).readFile(URI.revive(resource), opts); + return this._fsProvider.get(handle).readFile(URI.revive(resource)); }).then(data => { return Buffer.isBuffer(data) ? data.toString('base64') : Buffer.from(data.buffer, data.byteOffset, data.byteLength).toString('base64'); }); } - $writeFile(handle: number, resource: UriComponents, base64Content: string, opts: files.FileOptions): TPromise { + $writeFile(handle: number, resource: UriComponents, base64Content: string, opts: files.FileWriteOptions): TPromise { return asWinJsPromise(token => this._fsProvider.get(handle).writeFile(URI.revive(resource), Buffer.from(base64Content, 'base64'), opts)); } $delete(handle: number, resource: UriComponents): TPromise { - return asWinJsPromise(token => this._fsProvider.get(handle).delete(URI.revive(resource), {})); + return asWinJsPromise(token => this._fsProvider.get(handle).delete(URI.revive(resource), { recursive: true })); } $rename(handle: number, oldUri: UriComponents, newUri: UriComponents, opts: files.FileOverwriteOptions): TPromise { @@ -281,7 +281,7 @@ export class ExtHostFileSystem implements ExtHostFileSystemShape { } $mkdir(handle: number, resource: UriComponents): TPromise { - return asWinJsPromise(token => this._fsProvider.get(handle).createDirectory(URI.revive(resource), {})).then(ExtHostFileSystem._asIStat); + return asWinJsPromise(token => this._fsProvider.get(handle).createDirectory(URI.revive(resource))).then(ExtHostFileSystem._asIStat); } $watch(handle: number, session: number, resource: UriComponents, opts: files.IWatchOptions): void { diff --git a/src/vs/workbench/services/files/electron-browser/remoteFileService.ts b/src/vs/workbench/services/files/electron-browser/remoteFileService.ts index 7249a347a96..50accd1da5d 100644 --- a/src/vs/workbench/services/files/electron-browser/remoteFileService.ts +++ b/src/vs/workbench/services/files/electron-browser/remoteFileService.ts @@ -16,7 +16,7 @@ import { ITextResourceConfigurationService } from 'vs/editor/common/services/res import { localize } from 'vs/nls'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { FileChangesEvent, FileOperation, FileOperationError, FileOperationEvent, FileOperationResult, FileOptions, FileSystemProviderCapabilities, IContent, ICreateFileOptions, IFileStat, IFileSystemProvider, IFilesConfiguration, IResolveContentOptions, IResolveFileOptions, IResolveFileResult, IStat, IStreamContent, ITextSnapshot, IUpdateContentOptions, StringSnapshot, IWatchOptions, FileType } from 'vs/platform/files/common/files'; +import { FileChangesEvent, FileOperation, FileOperationError, FileOperationEvent, FileOperationResult, FileWriteOptions, FileSystemProviderCapabilities, IContent, ICreateFileOptions, IFileStat, IFileSystemProvider, IFilesConfiguration, IResolveContentOptions, IResolveFileOptions, IResolveFileResult, IStat, IStreamContent, ITextSnapshot, IUpdateContentOptions, StringSnapshot, IWatchOptions, FileType } from 'vs/platform/files/common/files'; import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { IStorageService } from 'vs/platform/storage/common/storage'; @@ -380,7 +380,7 @@ export class RemoteFileService extends FileService { } }; - const readable = createReadableOfProvider(provider, resource, options.position || 0, { read: true }); + const readable = createReadableOfProvider(provider, resource, options.position || 0); return toDecodeStream(readable, decodeStreamOpts).then(data => { @@ -414,7 +414,7 @@ export class RemoteFileService extends FileService { return this._withProvider(resource).then(provider => { const encoding = this.encoding.getWriteEncoding(resource); - return this._writeFile(provider, resource, new StringSnapshot(content), encoding, { write: true, create: true, exclusive: !(options && options.overwrite) }); + return this._writeFile(provider, resource, new StringSnapshot(content), encoding, { create: true, overwrite: Boolean(options && options.overwrite) }); }).then(fileStat => { this._onAfterOperation.fire(new FileOperationEvent(resource, FileOperation.CREATE, fileStat)); @@ -436,12 +436,12 @@ export class RemoteFileService extends FileService { } return this._withProvider(resource).then(provider => { const snapshot = typeof value === 'string' ? new StringSnapshot(value) : value; - return this._writeFile(provider, resource, snapshot, options && options.encoding, { write: true }); + return this._writeFile(provider, resource, snapshot, options && options.encoding, { create: false, overwrite: false }); }); } } - private _writeFile(provider: IFileSystemProvider, resource: URI, snapshot: ITextSnapshot, preferredEncoding: string, options: FileOptions): TPromise { + private _writeFile(provider: IFileSystemProvider, resource: URI, snapshot: ITextSnapshot, preferredEncoding: string, options: FileWriteOptions): TPromise { const readable = createReadableOfSnapshot(snapshot); const encoding = this.encoding.getWriteEncoding(resource, preferredEncoding); const decoder = decodeStream(encoding); @@ -592,7 +592,7 @@ export class RemoteFileService extends FileService { provider, target, new StringSnapshot(content.value), content.encoding, - { write: true, create: true, exclusive: !overwrite } + { create: true, overwrite } ).then(fileStat => { this._onAfterOperation.fire(new FileOperationEvent(source, FileOperation.COPY, fileStat)); return fileStat; diff --git a/src/vs/workbench/services/files/electron-browser/streams.ts b/src/vs/workbench/services/files/electron-browser/streams.ts index 583166be2cc..8e56b7da30e 100644 --- a/src/vs/workbench/services/files/electron-browser/streams.ts +++ b/src/vs/workbench/services/files/electron-browser/streams.ts @@ -7,10 +7,10 @@ import { Readable, Writable } from 'stream'; import { UTF8 } from 'vs/base/node/encoding'; import URI from 'vs/base/common/uri'; -import { IFileSystemProvider, ITextSnapshot, FileSystemProviderCapabilities, FileOptions } from 'vs/platform/files/common/files'; +import { IFileSystemProvider, ITextSnapshot, FileSystemProviderCapabilities, FileWriteOptions } from 'vs/platform/files/common/files'; import { illegalArgument } from 'vs/base/common/errors'; -export function createWritableOfProvider(provider: IFileSystemProvider, resource: URI, opts: FileOptions): Writable { +export function createWritableOfProvider(provider: IFileSystemProvider, resource: URI, opts: FileWriteOptions): Writable { if (provider.capabilities & FileSystemProviderCapabilities.FileOpenReadWriteClose) { return createWritable(provider, resource, opts); } else if (provider.capabilities & FileSystemProviderCapabilities.FileReadWrite) { @@ -20,7 +20,7 @@ export function createWritableOfProvider(provider: IFileSystemProvider, resource } } -function createSimpleWritable(provider: IFileSystemProvider, resource: URI, opts: FileOptions): Writable { +function createSimpleWritable(provider: IFileSystemProvider, resource: URI, opts: FileWriteOptions): Writable { return new class extends Writable { _chunks: Buffer[] = []; constructor(opts?) { @@ -41,7 +41,7 @@ function createSimpleWritable(provider: IFileSystemProvider, resource: URI, opts }; } -function createWritable(provider: IFileSystemProvider, resource: URI, opts: FileOptions): Writable { +function createWritable(provider: IFileSystemProvider, resource: URI, opts: FileWriteOptions): Writable { return new class extends Writable { _fd: number; _pos: number; @@ -51,7 +51,7 @@ function createWritable(provider: IFileSystemProvider, resource: URI, opts: File async _write(chunk: Buffer, encoding, callback: Function) { try { if (typeof this._fd !== 'number') { - this._fd = await provider.open(resource, opts); + this._fd = await provider.open(resource); } let bytesWritten = await provider.write(this._fd, this._pos, chunk, 0, chunk.length); this._pos += bytesWritten; @@ -70,17 +70,17 @@ function createWritable(provider: IFileSystemProvider, resource: URI, opts: File }; } -export function createReadableOfProvider(provider: IFileSystemProvider, resource: URI, position: number, opts: FileOptions): Readable { +export function createReadableOfProvider(provider: IFileSystemProvider, resource: URI, position: number): Readable { if (provider.capabilities & FileSystemProviderCapabilities.FileOpenReadWriteClose) { - return createReadable(provider, resource, position, opts); + return createReadable(provider, resource, position); } else if (provider.capabilities & FileSystemProviderCapabilities.FileReadWrite) { - return createSimpleReadable(provider, resource, position, opts); + return createSimpleReadable(provider, resource, position); } else { throw illegalArgument(); } } -function createReadable(provider: IFileSystemProvider, resource: URI, position: number, opts: FileOptions): Readable { +function createReadable(provider: IFileSystemProvider, resource: URI, position: number): Readable { return new class extends Readable { _fd: number; _pos: number = position; @@ -96,7 +96,7 @@ function createReadable(provider: IFileSystemProvider, resource: URI, position: this._reading = true; try { if (typeof this._fd !== 'number') { - this._fd = await provider.open(resource, opts); + this._fd = await provider.open(resource); } let buffer = Buffer.allocUnsafe(64 * 1024); while (this._reading) { @@ -124,14 +124,14 @@ function createReadable(provider: IFileSystemProvider, resource: URI, position: }; } -function createSimpleReadable(provider: IFileSystemProvider, resource: URI, position: number, opts: FileOptions): Readable { +function createSimpleReadable(provider: IFileSystemProvider, resource: URI, position: number): Readable { return new class extends Readable { _readOperation: Thenable; _read(size?: number): void { if (this._readOperation) { return; } - this._readOperation = provider.readFile(resource, opts).then(data => { + this._readOperation = provider.readFile(resource).then(data => { this.push(data.slice(position)); this.push(null); }, err => {