add storageUri and globalStorageUri to ExtensionContext, https://github.com/microsoft/vscode/issues/101857

This commit is contained in:
Johannes Rieken
2020-07-09 12:47:52 +02:00
parent a8a4e3760b
commit 43184b2bed
11 changed files with 188 additions and 153 deletions

View File

@@ -75,6 +75,7 @@ import { ExtHostAuthentication } from 'vs/workbench/api/common/extHostAuthentica
import { ExtHostTimeline } from 'vs/workbench/api/common/extHostTimeline';
import { ExtHostNotebookConcatDocument } from 'vs/workbench/api/common/extHostNotebookConcatDocument';
import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePaths';
import { IExtHostConsumerFileSystem } from 'vs/workbench/api/common/extHostFileSystemConsumer';
export interface IExtensionApiFactory {
(extension: IExtensionDescription, registry: ExtensionDescriptionRegistry, configProvider: ExtHostConfigProvider): typeof vscode;
@@ -87,6 +88,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
// services
const initData = accessor.get(IExtHostInitDataService);
const extHostConsumerFileSystem = accessor.get(IExtHostConsumerFileSystem);
const extensionService = accessor.get(IExtHostExtensionService);
const extHostWorkspace = accessor.get(IExtHostWorkspace);
const extHostConfiguration = accessor.get(IExtHostConfiguration);
@@ -746,7 +748,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
return extHostFileSystem.registerFileSystemProvider(scheme, provider, options);
},
get fs() {
return extHostFileSystem.fileSystem;
return extHostConsumerFileSystem;
},
registerFileSearchProvider: (scheme: string, provider: vscode.FileSearchProvider) => {
checkProposedApiEnabled(extension);

View File

@@ -386,14 +386,22 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
subscriptions: [],
get extensionUri() { return extensionDescription.extensionLocation; },
get extensionPath() { return extensionDescription.extensionLocation.fsPath; },
get storagePath() { return that._storagePath.workspaceValue(extensionDescription); },
get globalStoragePath() { return that._storagePath.globalValue(extensionDescription); },
asAbsolutePath(relativePath: string) { return path.join(extensionDescription.extensionLocation.fsPath, relativePath); },
get storagePath() { return that._storagePath.workspaceValue(extensionDescription)?.fsPath; },
get globalStoragePath() { return that._storagePath.globalValue(extensionDescription).fsPath; },
get logPath() { return path.join(that._initData.logsLocation.fsPath, extensionDescription.identifier.value); },
get logUri() {
checkProposedApiEnabled(extensionDescription);
return URI.joinPath(that._initData.logsLocation, extensionDescription.identifier.value);
},
get logPath() { return path.join(that._initData.logsLocation.fsPath, extensionDescription.identifier.value); },
get storageUri() {
checkProposedApiEnabled(extensionDescription);
return that._storagePath.workspaceValue(extensionDescription);
},
get globalStorageUri() {
checkProposedApiEnabled(extensionDescription);
return that._storagePath.globalValue(extensionDescription);
},
get extensionMode() { return extensionMode; },
get environmentVariableCollection() { return that._extHostTerminalService.getEnvironmentVariableCollection(extensionDescription); }
});

View File

@@ -8,7 +8,7 @@ import { MainContext, IMainContext, ExtHostFileSystemShape, MainThreadFileSystem
import type * as vscode from 'vscode';
import * as files from 'vs/platform/files/common/files';
import { IDisposable, toDisposable, dispose } from 'vs/base/common/lifecycle';
import { FileChangeType, FileSystemError } from 'vs/workbench/api/common/extHostTypes';
import { FileChangeType } from 'vs/workbench/api/common/extHostTypes';
import * as typeConverter from 'vs/workbench/api/common/extHostTypeConverters';
import { ExtHostLanguageFeatures } from 'vs/workbench/api/common/extHostLanguageFeatures';
import { Schemas } from 'vs/base/common/network';
@@ -108,59 +108,6 @@ class FsLinkProvider {
}
}
class ConsumerFileSystem implements vscode.FileSystem {
constructor(private _proxy: MainThreadFileSystemShape) { }
stat(uri: vscode.Uri): Promise<vscode.FileStat> {
return this._proxy.$stat(uri).catch(ConsumerFileSystem._handleError);
}
readDirectory(uri: vscode.Uri): Promise<[string, vscode.FileType][]> {
return this._proxy.$readdir(uri).catch(ConsumerFileSystem._handleError);
}
createDirectory(uri: vscode.Uri): Promise<void> {
return this._proxy.$mkdir(uri).catch(ConsumerFileSystem._handleError);
}
async readFile(uri: vscode.Uri): Promise<Uint8Array> {
return this._proxy.$readFile(uri).then(buff => buff.buffer).catch(ConsumerFileSystem._handleError);
}
writeFile(uri: vscode.Uri, content: Uint8Array): Promise<void> {
return this._proxy.$writeFile(uri, VSBuffer.wrap(content)).catch(ConsumerFileSystem._handleError);
}
delete(uri: vscode.Uri, options?: { recursive?: boolean; useTrash?: boolean; }): Promise<void> {
return this._proxy.$delete(uri, { ...{ recursive: false, useTrash: false }, ...options }).catch(ConsumerFileSystem._handleError);
}
rename(oldUri: vscode.Uri, newUri: vscode.Uri, options?: { overwrite?: boolean; }): Promise<void> {
return this._proxy.$rename(oldUri, newUri, { ...{ overwrite: false }, ...options }).catch(ConsumerFileSystem._handleError);
}
copy(source: vscode.Uri, destination: vscode.Uri, options?: { overwrite?: boolean }): Promise<void> {
return this._proxy.$copy(source, destination, { ...{ overwrite: false }, ...options }).catch(ConsumerFileSystem._handleError);
}
private static _handleError(err: any): never {
// generic error
if (!(err instanceof Error)) {
throw new FileSystemError(String(err));
}
// no provider (unknown scheme) error
if (err.name === 'ENOPRO') {
throw FileSystemError.Unavailable(err.message);
}
// file system error
switch (err.name) {
case files.FileSystemProviderErrorCode.FileExists: throw FileSystemError.FileExists(err.message);
case files.FileSystemProviderErrorCode.FileNotFound: throw FileSystemError.FileNotFound(err.message);
case files.FileSystemProviderErrorCode.FileNotADirectory: throw FileSystemError.FileNotADirectory(err.message);
case files.FileSystemProviderErrorCode.FileIsADirectory: throw FileSystemError.FileIsADirectory(err.message);
case files.FileSystemProviderErrorCode.NoPermissions: throw FileSystemError.NoPermissions(err.message);
case files.FileSystemProviderErrorCode.Unavailable: throw FileSystemError.Unavailable(err.message);
default: throw new FileSystemError(err.message, err.name as files.FileSystemProviderErrorCode);
}
}
}
export class ExtHostFileSystem implements ExtHostFileSystemShape {
private readonly _proxy: MainThreadFileSystemShape;
@@ -172,11 +119,8 @@ export class ExtHostFileSystem implements ExtHostFileSystemShape {
private _linkProviderRegistration?: IDisposable;
private _handlePool: number = 0;
readonly fileSystem: vscode.FileSystem;
constructor(mainContext: IMainContext, private _extHostLanguageFeatures: ExtHostLanguageFeatures) {
this._proxy = mainContext.getProxy(MainContext.MainThreadFileSystem);
this.fileSystem = new ConsumerFileSystem(this._proxy);
// register used schemes
Object.keys(Schemas).forEach(scheme => this._usedSchemes.add(scheme));

View File

@@ -0,0 +1,74 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { MainThreadFileSystemShape, MainContext } 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';
export class ExtHostConsumerFileSystem implements vscode.FileSystem {
readonly _serviceBrand: undefined;
private readonly _proxy: MainThreadFileSystemShape;
constructor(@IExtHostRpcService extHostRpc: IExtHostRpcService) {
this._proxy = extHostRpc.getProxy(MainContext.MainThreadFileSystem);
}
stat(uri: vscode.Uri): Promise<vscode.FileStat> {
return this._proxy.$stat(uri).catch(ExtHostConsumerFileSystem._handleError);
}
readDirectory(uri: vscode.Uri): Promise<[string, vscode.FileType][]> {
return this._proxy.$readdir(uri).catch(ExtHostConsumerFileSystem._handleError);
}
createDirectory(uri: vscode.Uri): Promise<void> {
return this._proxy.$mkdir(uri).catch(ExtHostConsumerFileSystem._handleError);
}
async readFile(uri: vscode.Uri): Promise<Uint8Array> {
return this._proxy.$readFile(uri).then(buff => buff.buffer).catch(ExtHostConsumerFileSystem._handleError);
}
writeFile(uri: vscode.Uri, content: Uint8Array): Promise<void> {
return this._proxy.$writeFile(uri, VSBuffer.wrap(content)).catch(ExtHostConsumerFileSystem._handleError);
}
delete(uri: vscode.Uri, options?: { recursive?: boolean; useTrash?: boolean; }): Promise<void> {
return this._proxy.$delete(uri, { ...{ recursive: false, useTrash: false }, ...options }).catch(ExtHostConsumerFileSystem._handleError);
}
rename(oldUri: vscode.Uri, newUri: vscode.Uri, options?: { overwrite?: boolean; }): Promise<void> {
return this._proxy.$rename(oldUri, newUri, { ...{ overwrite: false }, ...options }).catch(ExtHostConsumerFileSystem._handleError);
}
copy(source: vscode.Uri, destination: vscode.Uri, options?: { overwrite?: boolean; }): Promise<void> {
return this._proxy.$copy(source, destination, { ...{ overwrite: false }, ...options }).catch(ExtHostConsumerFileSystem._handleError);
}
private static _handleError(err: any): never {
// generic error
if (!(err instanceof Error)) {
throw new FileSystemError(String(err));
}
// no provider (unknown scheme) error
if (err.name === 'ENOPRO') {
throw FileSystemError.Unavailable(err.message);
}
// file system error
switch (err.name) {
case files.FileSystemProviderErrorCode.FileExists: throw FileSystemError.FileExists(err.message);
case files.FileSystemProviderErrorCode.FileNotFound: throw FileSystemError.FileNotFound(err.message);
case files.FileSystemProviderErrorCode.FileNotADirectory: throw FileSystemError.FileNotADirectory(err.message);
case files.FileSystemProviderErrorCode.FileIsADirectory: throw FileSystemError.FileIsADirectory(err.message);
case files.FileSystemProviderErrorCode.NoPermissions: throw FileSystemError.NoPermissions(err.message);
case files.FileSystemProviderErrorCode.Unavailable: throw FileSystemError.Unavailable(err.message);
default: throw new FileSystemError(err.message, err.name as files.FileSystemProviderErrorCode);
}
}
}
export interface IExtHostConsumerFileSystem extends ExtHostConsumerFileSystem { }
export const IExtHostConsumerFileSystem = createDecorator<IExtHostConsumerFileSystem>('IExtHostConsumerFileSystem');

View File

@@ -1020,7 +1020,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
if (provider) {
let storageRoot: URI | undefined;
if (this._extensionStoragePaths) {
storageRoot = URI.file(this._extensionStoragePaths.workspaceValue(provider.extension) ?? this._extensionStoragePaths.globalValue(provider.extension));
storageRoot = this._extensionStoragePaths.workspaceValue(provider.extension) ?? this._extensionStoragePaths.globalValue(provider.extension);
}
let document = this._documents.get(URI.revive(uri).toString());
@@ -1330,7 +1330,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
const entry = this._notebookContentProviders.get(viewType);
let storageRoot: URI | undefined;
if (entry && this._extensionStoragePaths) {
storageRoot = URI.file(this._extensionStoragePaths.workspaceValue(entry.extension) ?? this._extensionStoragePaths.globalValue(entry.extension));
storageRoot = this._extensionStoragePaths.workspaceValue(entry.extension) ?? this._extensionStoragePaths.globalValue(entry.extension);
}
if (!this._documents.has(revivedUriStr)) {

View File

@@ -5,12 +5,83 @@
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService';
import { ILogService } from 'vs/platform/log/common/log';
import { IEnvironment, IStaticWorkspaceData } from 'vs/workbench/api/common/extHost.protocol';
import { IExtHostConsumerFileSystem } from 'vs/workbench/api/common/extHostFileSystemConsumer';
import { URI } from 'vs/base/common/uri';
export const IExtensionStoragePaths = createDecorator<IExtensionStoragePaths>('IExtensionStoragePaths');
export interface IExtensionStoragePaths {
readonly _serviceBrand: undefined;
whenReady: Promise<any>;
workspaceValue(extension: IExtensionDescription): string | undefined;
globalValue(extension: IExtensionDescription): string;
workspaceValue(extension: IExtensionDescription): URI | undefined;
globalValue(extension: IExtensionDescription): URI;
}
export class ExtensionStoragePaths implements IExtensionStoragePaths {
readonly _serviceBrand: undefined;
private readonly _workspace?: IStaticWorkspaceData;
private readonly _environment: IEnvironment;
readonly whenReady: Promise<URI | undefined>;
private _value?: URI;
constructor(
@IExtHostInitDataService initData: IExtHostInitDataService,
@ILogService private readonly _logService: ILogService,
@IExtHostConsumerFileSystem private readonly _extHostFileSystem: IExtHostConsumerFileSystem
) {
this._workspace = initData.workspace ?? undefined;
this._environment = initData.environment;
this.whenReady = this._getOrCreateWorkspaceStoragePath().then(value => this._value = value);
}
private async _getOrCreateWorkspaceStoragePath(): Promise<URI | undefined> {
if (!this._workspace) {
return Promise.resolve(undefined);
}
const storageName = this._workspace.id;
const storageUri = URI.joinPath(this._environment.workspaceStorageHome, storageName);
try {
await this._extHostFileSystem.stat(storageUri);
this._logService.trace('[ExtHostStorage] storage dir already exists', storageUri);
return storageUri;
} catch {
// doesn't exist, that's OK
}
try {
this._logService.trace('[ExtHostStorage] creating dir and metadata-file', storageUri);
await this._extHostFileSystem.createDirectory(storageUri);
await this._extHostFileSystem.writeFile(
URI.joinPath(storageUri, 'meta.json'),
new TextEncoder().encode(JSON.stringify({
id: this._workspace.id,
configuration: URI.revive(this._workspace.configuration)?.toString(),
name: this._workspace.name
}, undefined, 2))
);
return storageUri;
} catch (e) {
this._logService.error('[ExtHostStorage]', e);
return undefined;
}
}
workspaceValue(extension: IExtensionDescription): URI | undefined {
if (this._value) {
return URI.joinPath(this._value, extension.identifier.value);
}
return undefined;
}
globalValue(extension: IExtensionDescription): URI {
return URI.joinPath(this._environment.globalStorageHome, extension.identifier.value.toLowerCase());
}
}

View File

@@ -598,7 +598,7 @@ export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape {
let storageRoot: URI | undefined;
if (this.supportEditing(entry.provider) && this._extensionStoragePaths) {
storageRoot = URI.file(this._extensionStoragePaths.workspaceValue(entry.extension) ?? this._extensionStoragePaths.globalValue(entry.extension));
storageRoot = this._extensionStoragePaths.workspaceValue(entry.extension) ?? this._extensionStoragePaths.globalValue(entry.extension);
}
this._documents.add(viewType, document, storageRoot);