Skip to content

Commit

Permalink
Merge pull request #136657 from microsoft/joh/shortcut_fs
Browse files Browse the repository at this point in the history
Add shortcut between file system provider and consumer
  • Loading branch information
jrieken authored Nov 18, 2021
2 parents 90a67d4 + ff04fe2 commit 8c64e1e
Show file tree
Hide file tree
Showing 4 changed files with 115 additions and 18 deletions.
4 changes: 4 additions & 0 deletions src/vs/workbench/api/browser/mainThreadFileSystem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,10 @@ export class MainThreadFileSystem implements MainThreadFileSystemShape {

throw err;
}

$ensureActivation(scheme: string): Promise<void> {
return this._fileService.activateProvider(scheme);
}
}

class RemoteFileSystemProvider implements IFileSystemProviderWithFileReadWriteCapability, IFileSystemProviderWithOpenReadWriteCloseCapability, IFileSystemProviderWithFileFolderCopyCapability {
Expand Down
6 changes: 5 additions & 1 deletion src/vs/workbench/api/common/extHost.api.impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ import { matchesScheme } from 'vs/platform/opener/common/opener';
import { ExtHostNotebookEditors } from 'vs/workbench/api/common/extHostNotebookEditors';
import { ExtHostNotebookDocuments } from 'vs/workbench/api/common/extHostNotebookDocuments';
import { ExtHostInteractive } from 'vs/workbench/api/common/extHostInteractive';
import { combinedDisposable } from 'vs/base/common/lifecycle';
import { checkProposedApiEnabled, isProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions';

export interface IExtensionApiFactory {
Expand Down Expand Up @@ -916,7 +917,10 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
return extHostTask.registerTaskProvider(extension, type, provider);
},
registerFileSystemProvider(scheme, provider, options) {
return extHostFileSystem.registerFileSystemProvider(extension, scheme, provider, options);
return combinedDisposable(
extHostFileSystem.registerFileSystemProvider(extension, scheme, provider, options),
extHostConsumerFileSystem.addFileSystemProvider(scheme, provider)
);
},
get fs() {
return extHostConsumerFileSystem.value;
Expand Down
2 changes: 2 additions & 0 deletions src/vs/workbench/api/common/extHost.protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1016,6 +1016,8 @@ export interface MainThreadFileSystemShape extends IDisposable {
$copy(resource: UriComponents, target: UriComponents, opts: files.FileOverwriteOptions): Promise<void>;
$mkdir(resource: UriComponents): Promise<void>;
$delete(resource: UriComponents, opts: files.FileDeleteOptions): Promise<void>;

$ensureActivation(scheme: string): Promise<void>;
}

export interface MainThreadLabelServiceShape extends IDisposable {
Expand Down
121 changes: 104 additions & 17 deletions src/vs/workbench/api/common/extHostFileSystemConsumer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,51 +3,126 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { MainContext } from './extHost.protocol';
import { MainContext, MainThreadFileSystemShape } from './extHost.protocol';
import * as vscode from 'vscode';
import * as files from 'vs/platform/files/common/files';
import { FileSystemError } from 'vs/workbench/api/common/extHostTypes';
import { VSBuffer } from 'vs/base/common/buffer';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
import { IExtHostFileSystemInfo } from 'vs/workbench/api/common/extHostFileSystemInfo';
import { IDisposable, toDisposable } from 'vs/base/common/lifecycle';

export class ExtHostConsumerFileSystem {

readonly _serviceBrand: undefined;

readonly value: vscode.FileSystem;

private readonly _proxy: MainThreadFileSystemShape;
private readonly _fileSystemProvider = new Map<string, vscode.FileSystemProvider>();

constructor(
@IExtHostRpcService extHostRpc: IExtHostRpcService,
@IExtHostFileSystemInfo fileSystemInfo: IExtHostFileSystemInfo,
) {
const proxy = extHostRpc.getProxy(MainContext.MainThreadFileSystem);
this._proxy = extHostRpc.getProxy(MainContext.MainThreadFileSystem);
const that = this;

this.value = Object.freeze({
stat(uri: vscode.Uri): Promise<vscode.FileStat> {
return proxy.$stat(uri).catch(ExtHostConsumerFileSystem._handleError);
async stat(uri: vscode.Uri): Promise<vscode.FileStat> {
try {
const provider = that._fileSystemProvider.get(uri.scheme);
if (!provider) {
return await that._proxy.$stat(uri);
}
// use shortcut
await that._proxy.$ensureActivation(uri.scheme);
const stat = await provider.stat(uri);
return <vscode.FileStat>{
type: stat.type,
ctime: stat.ctime,
mtime: stat.mtime,
size: stat.size,
permissions: stat.permissions
};
} catch (err) {
ExtHostConsumerFileSystem._handleError(err);
}
},
readDirectory(uri: vscode.Uri): Promise<[string, vscode.FileType][]> {
return proxy.$readdir(uri).catch(ExtHostConsumerFileSystem._handleError);
async readDirectory(uri: vscode.Uri): Promise<[string, vscode.FileType][]> {
try {
const provider = that._fileSystemProvider.get(uri.scheme);
if (provider) {
// use shortcut
await that._proxy.$ensureActivation(uri.scheme);
return (await provider.readDirectory(uri)).slice(); // safe-copy
} else {
return await that._proxy.$readdir(uri);
}
} catch (err) {
return ExtHostConsumerFileSystem._handleError(err);
}
},
createDirectory(uri: vscode.Uri): Promise<void> {
return proxy.$mkdir(uri).catch(ExtHostConsumerFileSystem._handleError);
async createDirectory(uri: vscode.Uri): Promise<void> {
try {
// no shortcut: does mkdirp
return await that._proxy.$mkdir(uri);
} catch (err) {
return ExtHostConsumerFileSystem._handleError(err);
}
},
async readFile(uri: vscode.Uri): Promise<Uint8Array> {
return proxy.$readFile(uri).then(buff => buff.buffer).catch(ExtHostConsumerFileSystem._handleError);
try {
const provider = that._fileSystemProvider.get(uri.scheme);
if (provider) {
// use shortcut
await that._proxy.$ensureActivation(uri.scheme);
return (await provider.readFile(uri)).slice(); // safe-copy
} else {
return that._proxy.$readFile(uri).then(buff => buff.buffer);
}
} catch (err) {
return ExtHostConsumerFileSystem._handleError(err);
}
},
writeFile(uri: vscode.Uri, content: Uint8Array): Promise<void> {
return proxy.$writeFile(uri, VSBuffer.wrap(content)).catch(ExtHostConsumerFileSystem._handleError);
async writeFile(uri: vscode.Uri, content: Uint8Array): Promise<void> {
try {
// no shortcut: does mkdirp
return await that._proxy.$writeFile(uri, VSBuffer.wrap(content));
} catch (err) {
return ExtHostConsumerFileSystem._handleError(err);
}
},
delete(uri: vscode.Uri, options?: { recursive?: boolean; useTrash?: boolean; }): Promise<void> {
return proxy.$delete(uri, { ...{ recursive: false, useTrash: false }, ...options }).catch(ExtHostConsumerFileSystem._handleError);
async delete(uri: vscode.Uri, options?: { recursive?: boolean; useTrash?: boolean; }): Promise<void> {
try {
const provider = that._fileSystemProvider.get(uri.scheme);
if (provider) {
// use shortcut
await that._proxy.$ensureActivation(uri.scheme);
return await provider.delete(uri, { recursive: false, ...options });
} else {
return await that._proxy.$delete(uri, { recursive: false, useTrash: false, ...options });
}
} catch (err) {
return ExtHostConsumerFileSystem._handleError(err);
}
},
rename(oldUri: vscode.Uri, newUri: vscode.Uri, options?: { overwrite?: boolean; }): Promise<void> {
return proxy.$rename(oldUri, newUri, { ...{ overwrite: false }, ...options }).catch(ExtHostConsumerFileSystem._handleError);
async rename(oldUri: vscode.Uri, newUri: vscode.Uri, options?: { overwrite?: boolean; }): Promise<void> {
try {
// no shortcut: potentially involves different schemes, does mkdirp
return await that._proxy.$rename(oldUri, newUri, { ...{ overwrite: false }, ...options });
} catch (err) {
return ExtHostConsumerFileSystem._handleError(err);
}
},
copy(source: vscode.Uri, destination: vscode.Uri, options?: { overwrite?: boolean; }): Promise<void> {
return proxy.$copy(source, destination, { ...{ overwrite: false }, ...options }).catch(ExtHostConsumerFileSystem._handleError);
async copy(source: vscode.Uri, destination: vscode.Uri, options?: { overwrite?: boolean; }): Promise<void> {
try {
// no shortcut: potentially involves different schemes, does mkdirp
return await that._proxy.$copy(source, destination, { ...{ overwrite: false }, ...options });
} catch (err) {
return ExtHostConsumerFileSystem._handleError(err);
}
},
isWritableFileSystem(scheme: string): boolean | undefined {
const capabilities = fileSystemInfo.getCapabilities(scheme);
Expand All @@ -60,6 +135,11 @@ export class ExtHostConsumerFileSystem {
}

private static _handleError(err: any): never {
// desired error type
if (err instanceof FileSystemError) {
throw err;
}

// generic error
if (!(err instanceof Error)) {
throw new FileSystemError(String(err));
Expand All @@ -82,6 +162,13 @@ export class ExtHostConsumerFileSystem {
default: throw new FileSystemError(err.message, err.name as files.FileSystemProviderErrorCode);
}
}

// ---

addFileSystemProvider(scheme: string, provider: vscode.FileSystemProvider): IDisposable {
this._fileSystemProvider.set(scheme, provider);
return toDisposable(() => this._fileSystemProvider.delete(scheme));
}
}

export interface IExtHostConsumerFileSystem extends ExtHostConsumerFileSystem { }
Expand Down

0 comments on commit 8c64e1e

Please sign in to comment.