From 4400f5affe4d8e6ce2b22687e77fc92d3f142d1d Mon Sep 17 00:00:00 2001 From: Tobias Ortmayr Date: Wed, 7 Oct 2020 10:05:19 +0200 Subject: [PATCH] Add documentation for `FileService` & `FileSystemProvider` fixes #8599 Contributed on behalf of STMicroelectronics Signed-off-by: Tobias Ortmayr Signed-off-by: Stefan Dirix --- .../filesystem/src/browser/file-service.ts | 118 ++++++++-- packages/filesystem/src/common/files.ts | 206 ++++++++++++++++++ 2 files changed, 311 insertions(+), 13 deletions(-) diff --git a/packages/filesystem/src/browser/file-service.ts b/packages/filesystem/src/browser/file-service.ts index de52a82846939..52da2ffc756ba 100644 --- a/packages/filesystem/src/browser/file-service.ts +++ b/packages/filesystem/src/browser/file-service.ts @@ -174,37 +174,72 @@ export interface UserFileOperationEvent extends WaitUntilEvent { } export const FileServiceContribution = Symbol('FileServiceContribution'); + +/** + * A {@link FileServiceContribution} can be used to add custom {@link FileSystemProvider}s. + * For this, the contribution has to listen to the {@link FileSystemProviderActivationEvent} and register + * the custom {@link FileSystemProvider}s according to the scheme when this event is fired. + * + * ### Example usage + * ```ts + * export class MyFileServiceContribution implements FileServiceContribution { + * registerFileSystemProviders(service: FileService): void { + * service.onWillActivateFileSystemProvider(event => { + * if (event.scheme === 'mySyncProviderScheme') { + * service.registerProvider('mySyncProviderScheme', this.mySyncProvider); + * } + * if (event.scheme === 'myAsyncProviderScheme') { + * event.waitUntil((async () => { + * const myAsyncProvider = await this.createAsyncProvider(); + * service.registerProvider('myAsyncProviderScheme', myAsyncProvider); + * })()); + * } + * }); + * + * } + *``` + */ export interface FileServiceContribution { /** - * ```ts - * service.onWillActivateFileSystemProvider(event => { - * if (event.scheme === 'mySyncProviderScheme') { - * service.registerProvider('mySyncProviderScheme', this.mySyncProvider); - * } - * if (event.scheme === 'myAsyncProviderScheme') { - * event.waitUntil((async () => { - * const myAsyncProvider = await this.createAsyncProvider(); - * service.registerProvider('myAsyncProviderScheme', myAsyncProvider); - * })()); - * } - * }); - * ``` + * Register custom file system providers for the given {@link FileService}. + * @param service The file service for which the providers should be registered. */ registerFileSystemProviders(service: FileService): void; } +/** + * Represents the `FileSystemProviderRegistration` event. + * This event is fired by the {@link FileService} if a {@link FileSystemProvider} is + * registered to or unregistered from the service. + */ export interface FileSystemProviderRegistrationEvent { + /** `True` if a new provider has been registered, `false` if a provider has been unregistered. */ added: boolean; + /** The (uri) scheme for which the provider was (previously) registered */ scheme: string; + /** The affected file system provider for which this event was fired. */ provider?: FileSystemProvider; } +/** + * Represents the `FileSystemProviderCapabilitiesChange` event. + * This event is fired by the {@link FileService} if the capabilities of one of its managed + * {@link FileSystemProvider}s have changed. + */ export interface FileSystemProviderCapabilitiesChangeEvent { + /** The affected file system provider for which this event was fired. */ provider: FileSystemProvider; + /** The (uri) scheme for which the provider is registered */ scheme: string; } +/** + * Represents the `FileSystemProviderActivation` event. + * This event is fired by the {@link FileService} if it wants to activate the + * {@link FileSystemProvider} for a specific scheme. + */ export interface FileSystemProviderActivationEvent extends WaitUntilEvent { + /** The (uri) scheme for which the provider should be activated */ scheme: string; } @@ -225,6 +260,12 @@ export class TextFileOperationError extends FileOperationError { } +/** + * The {@link FileService} is the common facade responsible for all interactions with file systems. + * It manages all registered {@link FileSystemProvider}s and + * forwards calls to the responsible {@link FileSystemProvider}, determined by the scheme. + * For additional documentation regarding the provided functions see also {@link FileSystemProvider}. + */ @injectable() export class FileService { @@ -302,6 +343,13 @@ export class FileService { private readonly providers = new Map(); private readonly activations = new Map>(); + /** + * Registers a new {@link FileSystemProvider} for the given scheme. + * @param scheme The (uri) scheme for which the provider should be registered. + * @param provider The file system provider that should be registered. + * + * @returns A `Disposable` that can be invoked to unregister the given provider. + */ registerProvider(scheme: string, provider: FileSystemProvider): Disposable { if (this.providers.has(scheme)) { throw new Error(`A filesystem provider for the scheme '${scheme}' is already registered.`); @@ -323,6 +371,12 @@ export class FileService { }); } + /** + * Try to activate the registered provider for the given scheme + * @param scheme The uri scheme for which the responsible provider should be activated. + * + * @returns A promise of the activated file system provider. Only resolves if a provider is available for this scheme, gets rejected otherwise. + */ async activateProvider(scheme: string): Promise { let provider = this.providers.get(scheme); if (provider) { @@ -347,10 +401,23 @@ export class FileService { return activation; } + /** + * Tests if the service (i.e. any of its registered {@link FileSystemProvider}s) can handle the given resource. + * @param resource `URI` of the resource to test. + * + * @returns `true` if the resource can be handled, `false` otherwise. + */ canHandleResource(resource: URI): boolean { return this.providers.has(resource.scheme); } + /** + * Tests if the service (i.e the {@link FileSystemProvider} registered for the given uri scheme) provides the given capability. + * @param resource `URI` of the resource to test. + * @param capability The required capability. + * + * @returns `true` if the resource can be handled and the required cabability can be provided. + */ hasCapability(resource: URI, capability: FileSystemProviderCapabilities): boolean { const provider = this.providers.get(resource.scheme); @@ -394,6 +461,13 @@ export class FileService { */ readonly onDidRunOperation = this.onDidRunOperationEmitter.event; + /** + * Try to resolve file information and metadata for the given resource. + * @param resource `URI` of the resource that should be resolved. + * @param options Options to customize the resolvement process. + * + * @return A promise that resolves if the resource could be successfully resolved. + */ resolve(resource: URI, options: ResolveMetadataFileOptions): Promise; resolve(resource: URI, options?: ResolveFileOptions | undefined): Promise; async resolve(resource: any, options?: any) { @@ -485,6 +559,13 @@ export class FileService { return fileStat; } + /** + * Try to resolve file information and metadata for all given resource. + * @param toResolve An array of all the resources (and corresponding resolvement options) that should be resolved. + * + * @returns A promise of all resolved resources. The promise is not rejected if any of the given resources cannot be resolved. + * Instead this is reflected with the `success` flag of the corresponding {@link ResolveFileResult}. + */ async resolveAll(toResolve: { resource: URI, options?: ResolveFileOptions }[]): Promise; async resolveAll(toResolve: { resource: URI, options: ResolveMetadataFileOptions }[]): Promise; async resolveAll(toResolve: { resource: URI; options?: ResolveFileOptions; }[]): Promise { @@ -499,6 +580,13 @@ export class FileService { })); } + /** + * Tests if the given resource exists in the filesystem. + * @param resource `URI` of the resource which should be tested. + * @throws Will throw an error if no {@link FileSystemProvider} is registered for the given resource. + * + * @returns A promise that resolves to `true` if the resource exists. + */ async exists(resource: URI): Promise { const provider = await this.withProvider(resource); @@ -536,6 +624,10 @@ export class FileService { * interact with the OS, e.g. when running a command on the shell. * * If you need to display human readable simple or long names then use `LabelProvider` instead. + * @param resource `URI` of the resource that should be resolved. + * @throws Will throw an error if no {@link FileSystemProvider} is registered for the given resource. + * + * @returns A promise of the resolved fs path. */ async fsPath(resource: URI): Promise { const provider = await this.withProvider(resource); diff --git a/packages/filesystem/src/common/files.ts b/packages/filesystem/src/common/files.ts index 4df3dccfc5413..7258e6700dbed 100644 --- a/packages/filesystem/src/common/files.ts +++ b/packages/filesystem/src/common/files.ts @@ -558,41 +558,161 @@ export function ensureFileSystemProviderError(error?: Error): Error { } export const FileSystemProvider = Symbol('FileSystemProvider'); +/** + * A {@link FileSystemProvider} provides the capabilities to read, write, discover, and to manage files and folders + * of the underlying (potentially virtual) file system. {@link FileSystemProvider}s can be used to serve files from both the + * local disk as well as remote locations like ftp-servers, REST-services etc. A {@link FileSystemProvider} is registered for a certain + * scheme and can handle all resources whose uri does conform to that scheme. + */ export interface FileSystemProvider { + /** The {@link FileSystemProviderCapabilities} for this provider. */ readonly capabilities: FileSystemProviderCapabilities; + + /** * Event that is fired if the capabilities of this provider have changed. */ readonly onDidChangeCapabilities: Event; + /** Event that is fired if a (watched) file in the filesystem of this provider has changed. */ readonly onDidChangeFile: Event; + + /** Event that is fired if an error occurred when watching files in the filesystem of this provider. */ readonly onFileWatchError: Event; + + /** + * Watch the given resource and react to changes by firing the {@link FileSystemProvider#onDidChangeFile} event. + * @param resource `URI` of the resource to be watched. + * @param opts Options to define if the resource should be watched recursively and to + * provide a set of resources that should be excluded from watching. + * + * @returns A `Disposable` that can be invoked to stop watching the resource. + */ watch(resource: URI, opts: WatchOptions): IDisposable; + /** + * Retrieve metadata about a given file. + * + * @param uri The `URI` of the file to retrieve meta data about. + * @returns A promise of the metadata about the resource. + */ stat(resource: URI): Promise; + + /** + * Create a new directory using the given resource uri. + * @param resource The `URI` of the new folder. + */ mkdir(resource: URI): Promise; + + /** + * Retrieve the content of a given directory. + * @param resource The `URI` of the directory. + * + * @returns A map containing the {@link FileType} for each child resource (uri). + */ readdir(resource: URI): Promise<[string, FileType][]>; + + /** + * Delete the given resource. + * @param resource The `URI` of the resource to delete. + * @param opts Options to define if files should be deleted recursively and if the trash should be used. + */ delete(resource: URI, opts: FileDeleteOptions): Promise; + /** + * Rename a file or folder. + * @param from `URI` of the existing file or folder. + * @param to `URI` of the target location. + * @param opts Options to define if existing files should be overwritten. + */ + rename(from: URI, to: URI, opts: FileOverwriteOptions): Promise; + + /** + * Optional function that has to be implemented by {@link FileSystemProviderWithFileFolderCopyCapability}. + * see {@link FileSystemProviderWithFileFolderCopyCapability#copy}} for additional documentation. + */ copy?(from: URI, to: URI, opts: FileOverwriteOptions): Promise; + /** + * Optional function that has to be implemented by {@link FileSystemProviderWithFileReadWriteCapability}. + * see {@link FileSystemProviderWithFileReadWriteCapability#readFile} for additional documentation. + */ readFile?(resource: URI): Promise; + + /** + * Optional function that has to be implemented by {@link FileSystemProviderWithFileReadWriteCapability}. + * see {@link FileSystemProviderWithFileReadWriteCapability#writeFile} for additional documentation. + */ writeFile?(resource: URI, content: Uint8Array, opts: FileWriteOptions): Promise; + /** + * Optional function that has to be implemented by {@link FileSystemProviderWithFileReadStreamCapability}. + * see {@link FileSystemProviderWithFileReadStreamCapability#readFileStream} for additional documentation. + */ readFileStream?(resource: URI, opts: FileReadStreamOptions, token: CancellationToken): ReadableStreamEvents; + /** + * Optional function that has to be implemented by {@link FileSystemProviderWithOpenReadWriteCloseCapability}. + * see {@link FileSystemProviderWithOpenReadWriteCloseCapability#open} for additional documentation. + */ open?(resource: URI, opts: FileOpenOptions): Promise; + + /** + * Optional function that has to be implemented by {@link FileSystemProviderWithOpenReadWriteCloseCapability}. + * see {@link FileSystemProviderWithOpenReadWriteCloseCapability#close} for additional documentation. + */ close?(fd: number): Promise; + + /** + * Optional function that has to be implemented by {@link FileSystemProviderWithOpenReadWriteCloseCapability}. + * see {@link FileSystemProviderWithOpenReadWriteCloseCapability#read} for additional documentation. + */ read?(fd: number, pos: number, data: Uint8Array, offset: number, length: number): Promise; + + /** + * Optional function that has to be implemented by {@link FileSystemProviderWithOpenReadWriteCloseCapability}. + * see {@link FileSystemProviderWithOpenReadWriteCloseCapability#write} for additional documentation. + */ write?(fd: number, pos: number, data: Uint8Array, offset: number, length: number): Promise; + /** + * Optional function that has to be implemented by {@link FileSystemProviderWithAccessCapability}. + * see {@link FileSystemProviderWithAccessCapability#access} for additional documentation. + */ access?(resource: URI, mode?: number): Promise; + + /** + * Optional function that has to be implemented by {@link FileSystemProviderWithAccessCapability}. + * see {@link FileSystemProviderWithAccessCapability#fsPath} for additional documentation. + */ fsPath?(resource: URI): Promise; + /** + * Optional function that has to be implemented by {@link FileSystemProviderWithUpdateCapability}. + * see {@link FileSystemProviderWithUpdateCapability#updateFile} for additional documentation. + */ updateFile?(resource: URI, changes: TextDocumentContentChangeEvent[], opts: FileUpdateOptions): Promise; } +/** + * Subtype of {@link FileSystemProvider} that ensures that the optional functions needed for providers, that should be + * able access files, are implemented. + */ export interface FileSystemProviderWithAccessCapability extends FileSystemProvider { + /** + * Test if the user has the permission to access the given file in the specified mode. + * @param resource The `URI` of the file that should be tested. + * @param mode The access mode that should be tested. + * + * @returns A promise that resolves if the user has the required permissions, should be rejected otherwise. + */ access(resource: URI, mode?: number): Promise; + + /** + * Derive the platform specific file system path that is represented by the resource. + * @param resource `URI` of the resource to derive the path from. + * + * @returns A promise of the corresponding file system path. + */ fsPath(resource: URI): Promise; } @@ -600,7 +720,19 @@ export function hasAccessCapability(provider: FileSystemProvider): provider is F return !!(provider.capabilities & FileSystemProviderCapabilities.Access); } +/** + * Subtype of {@link FileSystemProvider} that ensures that the optional functions needed, for providers that should be + * able to update (text) files, are implemented. + */ export interface FileSystemProviderWithUpdateCapability extends FileSystemProvider { + /** + * Update the content of the given (text) file according to the given text document changes. + * @param resource `URI` of the resource to update. + * @param changes Array of events describing the changes to the file. + * @param opts The encoding options. + * + * @returns A promise of the file metadata that resolves after the update process has completed. + */ updateFile(resource: URI, changes: TextDocumentContentChangeEvent[], opts: FileUpdateOptions): Promise; } @@ -608,8 +740,25 @@ export function hasUpdateCapability(provider: FileSystemProvider): provider is F return !!(provider.capabilities & FileSystemProviderCapabilities.Update); } +/** + * Subtype of {@link FileSystemProvider} that ensures that the optional functions, needed for providers + * that should be able to read & write files, are implemented. + */ export interface FileSystemProviderWithFileReadWriteCapability extends FileSystemProvider { + /** + * Read the contents of the given file as stream. + * @param resource The `URI` of the file. + * + * @return The `ReadableStreamEvents` for the readable stream of the given file. + */ readFile(resource: URI): Promise; + + /** + * Write data to a file, replacing its entire contents. + * @param resource The uri of the file. + * @param content The new content of the file. + * @param opts Options to define if the file should be created if missing and if an existing file should be overwritten. + */ writeFile(resource: URI, content: Uint8Array, opts: FileWriteOptions): Promise; } @@ -617,7 +766,17 @@ export function hasReadWriteCapability(provider: FileSystemProvider): provider i return !!(provider.capabilities & FileSystemProviderCapabilities.FileReadWrite); } +/** + * Subtype of {@link FileSystemProvider} that ensures that the optional functions, needed for providers that should be able to copy + * file folders, are implemented. + */ export interface FileSystemProviderWithFileFolderCopyCapability extends FileSystemProvider { + /** + * Copy files or folders. + * @param from `URI` of the existing file or folder. + * @param to `URI` of the destination location. + * @param opts Options to define if existing files should be overwritten. + */ copy(from: URI, to: URI, opts: FileOverwriteOptions): Promise; } @@ -625,10 +784,47 @@ export function hasFileFolderCopyCapability(provider: FileSystemProvider): provi return !!(provider.capabilities & FileSystemProviderCapabilities.FileFolderCopy); } +/** + * Subtype of {@link FileSystemProvider} that ensures that the optional functions, needed for providers that should be able to open,read, write + * or close files, are implemented. + */ export interface FileSystemProviderWithOpenReadWriteCloseCapability extends FileSystemProvider { + /** + * Open the give file. + * @param resource The `URI` of the file to open. + * @param opts Options to define if the file should be created if it does not exist yet. + * + * @returns A promise of the file descriptor that resolves after the file is open. + */ open(resource: URI, opts: FileOpenOptions): Promise; + + /** + * Close the file with the given file descriptor. + * @param fd the file descriptor to close. + */ close(fd: number): Promise; + + /** + * Read specified content from a given file descriptor into a data buffer. + * @param fd The file descriptor referencing the file to read from. + * @param pos The offset from the beginning of the file from which data should be read. + * @param data The buffer that the data will be written to. + * @param offset The offset in the buffer at which to start writing. + * @param length The number of bytes to read. + * + * @returns A promise of the number of bytes read. + */ read(fd: number, pos: number, data: Uint8Array, offset: number, length: number): Promise; + + /** + * Write specified content from the data buffer to the file referenced by the given file descriptor. + * @param fd The file descriptor referencing the file to write to. + * @param pos The offset from the beginning of the file where this data should be written. + * @param offset The part of the buffer to be read from. + * @param length The number of bytes to write. + * + * @returns A promise of the number of bytes written. + */ write(fd: number, pos: number, data: Uint8Array, offset: number, length: number): Promise; } @@ -636,7 +832,17 @@ export function hasOpenReadWriteCloseCapability(provider: FileSystemProvider): p return !!(provider.capabilities & FileSystemProviderCapabilities.FileOpenReadWriteClose); } +/** + * Subtype of {@link FileSystemProvider} that ensures that the optional functions, needed for providers that should be able to read + * files as streams, are implemented. + */ export interface FileSystemProviderWithFileReadStreamCapability extends FileSystemProvider { + /** + * Read the contents of the given file as stream. + * @param resource The `URI` of the file. + * + * @return The `ReadableStreamEvents` for the readable stream of the given file. + */ readFileStream(resource: URI, opts: FileReadStreamOptions, token: CancellationToken): ReadableStreamEvents; }