提交 49852131 编写于 作者: S Sandeep Somavarapu

make user data provider a full fledged filesystem provider

上级 60dcb1ac
...@@ -19,7 +19,7 @@ import { RemoteAgentService } from 'vs/workbench/services/remote/browser/remoteA ...@@ -19,7 +19,7 @@ import { RemoteAgentService } from 'vs/workbench/services/remote/browser/remoteA
import { RemoteAuthorityResolverService } from 'vs/platform/remote/browser/remoteAuthorityResolverService'; import { RemoteAuthorityResolverService } from 'vs/platform/remote/browser/remoteAuthorityResolverService';
import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver'; import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver';
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
import { IFileService } from 'vs/platform/files/common/files'; import { IFileService, IFileSystemProvider } from 'vs/platform/files/common/files';
import { FileService } from 'vs/workbench/services/files/common/fileService'; import { FileService } from 'vs/workbench/services/files/common/fileService';
import { Schemas } from 'vs/base/common/network'; import { Schemas } from 'vs/base/common/network';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
...@@ -35,10 +35,7 @@ import { hash } from 'vs/base/common/hash'; ...@@ -35,10 +35,7 @@ import { hash } from 'vs/base/common/hash';
import { IWorkbenchConstructionOptions } from 'vs/workbench/workbench.web.api'; import { IWorkbenchConstructionOptions } from 'vs/workbench/workbench.web.api';
import { ProductService } from 'vs/platform/product/browser/productService'; import { ProductService } from 'vs/platform/product/browser/productService';
import { FileUserDataProvider } from 'vs/workbench/services/userData/common/fileUserDataProvider'; import { FileUserDataProvider } from 'vs/workbench/services/userData/common/fileUserDataProvider';
import { UserDataFileSystemProvider } from 'vs/workbench/services/userData/common/userDataFileSystemProvider'; import { joinPath, dirname } from 'vs/base/common/resources';
import { joinPath } from 'vs/base/common/resources';
import { InMemoryUserDataProvider } from 'vs/workbench/services/userData/common/inMemoryUserDataProvider';
import { IUserDataProvider } from 'vs/workbench/services/userData/common/userData';
class CodeRendererMain extends Disposable { class CodeRendererMain extends Disposable {
...@@ -110,16 +107,26 @@ class CodeRendererMain extends Disposable { ...@@ -110,16 +107,26 @@ class CodeRendererMain extends Disposable {
const fileService = this._register(new FileService(logService)); const fileService = this._register(new FileService(logService));
serviceCollection.set(IFileService, fileService); serviceCollection.set(IFileService, fileService);
let userDataProvider: IFileSystemProvider | undefined = this.configuration.userDataProvider;
const connection = remoteAgentService.getConnection(); const connection = remoteAgentService.getConnection();
if (connection) { if (connection) {
const channel = connection.getChannel<IChannel>(REMOTE_FILE_SYSTEM_CHANNEL_NAME); const channel = connection.getChannel<IChannel>(REMOTE_FILE_SYSTEM_CHANNEL_NAME);
const remoteFileSystemProvider = this._register(new RemoteExtensionsFileSystemProvider(channel, remoteAgentService.getEnvironment())); const remoteFileSystemProvider = this._register(new RemoteExtensionsFileSystemProvider(channel, remoteAgentService.getEnvironment()));
fileService.registerProvider(Schemas.vscodeRemote, remoteFileSystemProvider); fileService.registerProvider(Schemas.vscodeRemote, remoteFileSystemProvider);
if (!userDataProvider) {
const remoteUserDataUri = this.getRemoteUserDataUri();
if (remoteUserDataUri) {
userDataProvider = this._register(new FileUserDataProvider(remoteUserDataUri, dirname(remoteUserDataUri), remoteFileSystemProvider));
}
}
} }
// User Data Provider // User Data Provider
fileService.registerProvider(Schemas.userData, new UserDataFileSystemProvider(environmentService.userRoamingDataHome, this.getUserDataPovider(fileService))); if (userDataProvider) {
fileService.registerProvider(Schemas.userData, userDataProvider);
}
const payload = await this.resolveWorkspaceInitializationPayload(); const payload = await this.resolveWorkspaceInitializationPayload();
...@@ -169,21 +176,6 @@ class CodeRendererMain extends Disposable { ...@@ -169,21 +176,6 @@ class CodeRendererMain extends Disposable {
return { id: 'empty-window' }; return { id: 'empty-window' };
} }
private getUserDataPovider(fileService: IFileService): IUserDataProvider {
if (this.configuration.userDataProvider) {
return this.configuration.userDataProvider;
}
if (this.configuration.remoteAuthority) {
const remoteUserDataUri = this.getRemoteUserDataUri();
if (remoteUserDataUri) {
return this._register(new FileUserDataProvider(remoteUserDataUri, fileService));
}
}
return this._register(new InMemoryUserDataProvider());
}
private getRemoteUserDataUri(): URI | null { private getRemoteUserDataUri(): URI | null {
const element = document.getElementById('vscode-remote-user-data-uri'); const element = document.getElementById('vscode-remote-user-data-uri');
if (element) { if (element) {
......
...@@ -10,12 +10,9 @@ import * as nls from 'vs/nls'; ...@@ -10,12 +10,9 @@ import * as nls from 'vs/nls';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { LanguageId } from 'vs/editor/common/modes'; import { LanguageId } from 'vs/editor/common/modes';
import { SnippetFile, Snippet } from 'vs/workbench/contrib/snippets/browser/snippetsFile'; import { SnippetFile, Snippet } from 'vs/workbench/contrib/snippets/browser/snippetsFile';
import { IUserDataContainerRegistry, Extensions } from 'vs/workbench/services/userData/common/userData';
export const ISnippetsService = createDecorator<ISnippetsService>('snippetService'); export const ISnippetsService = createDecorator<ISnippetsService>('snippetService');
Registry.as<IUserDataContainerRegistry>(Extensions.UserDataContainers).registerContainer('snippets');
export interface ISnippetsService { export interface ISnippetsService {
_serviceBrand: any; _serviceBrand: any;
......
...@@ -51,7 +51,6 @@ import { SpdLogService } from 'vs/platform/log/node/spdlogService'; ...@@ -51,7 +51,6 @@ import { SpdLogService } from 'vs/platform/log/node/spdlogService';
import { SignService } from 'vs/platform/sign/node/signService'; import { SignService } from 'vs/platform/sign/node/signService';
import { ISignService } from 'vs/platform/sign/common/sign'; import { ISignService } from 'vs/platform/sign/common/sign';
import { FileUserDataProvider } from 'vs/workbench/services/userData/common/fileUserDataProvider'; import { FileUserDataProvider } from 'vs/workbench/services/userData/common/fileUserDataProvider';
import { UserDataFileSystemProvider } from 'vs/workbench/services/userData/common/userDataFileSystemProvider';
class CodeRendererMain extends Disposable { class CodeRendererMain extends Disposable {
...@@ -200,6 +199,9 @@ class CodeRendererMain extends Disposable { ...@@ -200,6 +199,9 @@ class CodeRendererMain extends Disposable {
const diskFileSystemProvider = this._register(new DiskFileSystemProvider(logService)); const diskFileSystemProvider = this._register(new DiskFileSystemProvider(logService));
fileService.registerProvider(Schemas.file, diskFileSystemProvider); fileService.registerProvider(Schemas.file, diskFileSystemProvider);
// User Data Provider
fileService.registerProvider(Schemas.userData, new FileUserDataProvider(environmentService.appSettingsHome, URI.file(environmentService.backupHome), diskFileSystemProvider));
const connection = remoteAgentService.getConnection(); const connection = remoteAgentService.getConnection();
if (connection) { if (connection) {
const channel = connection.getChannel<IChannel>(REMOTE_FILE_SYSTEM_CHANNEL_NAME); const channel = connection.getChannel<IChannel>(REMOTE_FILE_SYSTEM_CHANNEL_NAME);
...@@ -207,8 +209,6 @@ class CodeRendererMain extends Disposable { ...@@ -207,8 +209,6 @@ class CodeRendererMain extends Disposable {
fileService.registerProvider(Schemas.vscodeRemote, remoteFileSystemProvider); fileService.registerProvider(Schemas.vscodeRemote, remoteFileSystemProvider);
} }
// User Data Provider
fileService.registerProvider(Schemas.userData, new UserDataFileSystemProvider(environmentService.userRoamingDataHome, new FileUserDataProvider(environmentService.appSettingsHome, fileService)));
const payload = await this.resolveWorkspaceInitializationPayload(environmentService); const payload = await this.resolveWorkspaceInitializationPayload(environmentService);
......
...@@ -39,10 +39,20 @@ import { DiskFileSystemProvider } from 'vs/workbench/services/files/node/diskFil ...@@ -39,10 +39,20 @@ import { DiskFileSystemProvider } from 'vs/workbench/services/files/node/diskFil
import { IFileService } from 'vs/platform/files/common/files'; import { IFileService } from 'vs/platform/files/common/files';
import { ConfigurationCache } from 'vs/workbench/services/configuration/node/configurationCache'; import { ConfigurationCache } from 'vs/workbench/services/configuration/node/configurationCache';
import { KeybindingsEditingService, IKeybindingEditingService } from 'vs/workbench/services/keybinding/common/keybindingEditing'; import { KeybindingsEditingService, IKeybindingEditingService } from 'vs/workbench/services/keybinding/common/keybindingEditing';
import { UserDataFileSystemProvider } from 'vs/workbench/services/userData/common/userDataFileSystemProvider';
import { WorkbenchEnvironmentService } from 'vs/workbench/services/environment/node/environmentService'; import { WorkbenchEnvironmentService } from 'vs/workbench/services/environment/node/environmentService';
import { IWindowConfiguration } from 'vs/platform/windows/common/windows'; import { IWindowConfiguration } from 'vs/platform/windows/common/windows';
import { FileUserDataProvider } from 'vs/workbench/services/userData/common/fileUserDataProvider'; import { FileUserDataProvider } from 'vs/workbench/services/userData/common/fileUserDataProvider';
import { dirname } from 'vs/base/common/resources';
class TestBackupEnvironmentService extends WorkbenchEnvironmentService {
constructor(private _appSettingsHome: URI) {
super(parseArgs(process.argv) as IWindowConfiguration, process.execPath);
}
get appSettingsHome() { return this._appSettingsHome; }
}
suite('ConfigurationEditingService', () => { suite('ConfigurationEditingService', () => {
...@@ -95,14 +105,15 @@ suite('ConfigurationEditingService', () => { ...@@ -95,14 +105,15 @@ suite('ConfigurationEditingService', () => {
clearServices(); clearServices();
instantiationService = <TestInstantiationService>workbenchInstantiationService(); instantiationService = <TestInstantiationService>workbenchInstantiationService();
const environmentService = new WorkbenchEnvironmentService(<IWindowConfiguration>parseArgs(process.argv), process.execPath); const environmentService = new TestBackupEnvironmentService(URI.file(workspaceDir));
instantiationService.stub(IEnvironmentService, environmentService); instantiationService.stub(IEnvironmentService, environmentService);
const remoteAgentService = instantiationService.createInstance(RemoteAgentService, {}); const remoteAgentService = instantiationService.createInstance(RemoteAgentService, {});
const fileService = new FileService(new NullLogService()); const fileService = new FileService(new NullLogService());
fileService.registerProvider(Schemas.file, new DiskFileSystemProvider(new NullLogService())); const diskFileSystemProvider = new DiskFileSystemProvider(new NullLogService());
fileService.registerProvider(Schemas.file, diskFileSystemProvider);
fileService.registerProvider(Schemas.userData, new FileUserDataProvider(URI.file(workspaceDir), dirname(URI.file(workspaceDir)), diskFileSystemProvider));
instantiationService.stub(IFileService, fileService); instantiationService.stub(IFileService, fileService);
instantiationService.stub(IRemoteAgentService, remoteAgentService); instantiationService.stub(IRemoteAgentService, remoteAgentService);
fileService.registerProvider(Schemas.userData, new UserDataFileSystemProvider(environmentService.userRoamingDataHome, new FileUserDataProvider(URI.file(workspaceDir), fileService)));
const workspaceService = new WorkspaceService({ configurationCache: new ConfigurationCache(environmentService) }, environmentService, fileService, remoteAgentService); const workspaceService = new WorkspaceService({ configurationCache: new ConfigurationCache(environmentService) }, environmentService, fileService, remoteAgentService);
instantiationService.stub(IWorkspaceContextService, workspaceService); instantiationService.stub(IWorkspaceContextService, workspaceService);
return workspaceService.initialize(noWorkspace ? { id: '' } : { folder: URI.file(workspaceDir), id: createHash('md5').update(URI.file(workspaceDir).toString()).digest('hex') }).then(() => { return workspaceService.initialize(noWorkspace ? { id: '' } : { folder: URI.file(workspaceDir), id: createHash('md5').update(URI.file(workspaceDir).toString()).digest('hex') }).then(() => {
......
...@@ -30,7 +30,7 @@ import { IJSONEditingService } from 'vs/workbench/services/configuration/common/ ...@@ -30,7 +30,7 @@ import { IJSONEditingService } from 'vs/workbench/services/configuration/common/
import { JSONEditingService } from 'vs/workbench/services/configuration/common/jsonEditingService'; import { JSONEditingService } from 'vs/workbench/services/configuration/common/jsonEditingService';
import { createHash } from 'crypto'; import { createHash } from 'crypto';
import { Schemas } from 'vs/base/common/network'; import { Schemas } from 'vs/base/common/network';
import { originalFSPath } from 'vs/base/common/resources'; import { originalFSPath, dirname } from 'vs/base/common/resources';
import { isLinux } from 'vs/base/common/platform'; import { isLinux } from 'vs/base/common/platform';
import { IWindowConfiguration } from 'vs/platform/windows/common/windows'; import { IWindowConfiguration } from 'vs/platform/windows/common/windows';
import { RemoteAgentService } from 'vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl'; import { RemoteAgentService } from 'vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl';
...@@ -47,9 +47,18 @@ import { SignService } from 'vs/platform/sign/browser/signService'; ...@@ -47,9 +47,18 @@ import { SignService } from 'vs/platform/sign/browser/signService';
import { FileUserDataProvider } from 'vs/workbench/services/userData/common/fileUserDataProvider'; import { FileUserDataProvider } from 'vs/workbench/services/userData/common/fileUserDataProvider';
import { IKeybindingEditingService, KeybindingsEditingService } from 'vs/workbench/services/keybinding/common/keybindingEditing'; import { IKeybindingEditingService, KeybindingsEditingService } from 'vs/workbench/services/keybinding/common/keybindingEditing';
import { WorkbenchEnvironmentService } from 'vs/workbench/services/environment/node/environmentService'; import { WorkbenchEnvironmentService } from 'vs/workbench/services/environment/node/environmentService';
import { UserDataFileSystemProvider } from 'vs/workbench/services/userData/common/userDataFileSystemProvider';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
class TestEnvironmentService extends WorkbenchEnvironmentService {
constructor(private _appSettingsHome: URI) {
super(parseArgs(process.argv) as IWindowConfiguration, process.execPath);
}
get appSettingsHome() { return this._appSettingsHome; }
}
function setUpFolderWorkspace(folderName: string): Promise<{ parentDir: string, folderDir: string }> { function setUpFolderWorkspace(folderName: string): Promise<{ parentDir: string, folderDir: string }> {
const id = uuid.generateUuid(); const id = uuid.generateUuid();
const parentDir = path.join(os.tmpdir(), 'vsctests', id); const parentDir = path.join(os.tmpdir(), 'vsctests', id);
...@@ -96,9 +105,9 @@ suite('WorkspaceContextService - Folder', () => { ...@@ -96,9 +105,9 @@ suite('WorkspaceContextService - Folder', () => {
.then(({ parentDir, folderDir }) => { .then(({ parentDir, folderDir }) => {
parentResource = parentDir; parentResource = parentDir;
workspaceResource = folderDir; workspaceResource = folderDir;
const environmentService = new WorkbenchEnvironmentService(<IWindowConfiguration>parseArgs(process.argv), process.execPath); const environmentService = new TestEnvironmentService(URI.file(parentDir));
const fileService = new FileService(new NullLogService()); const fileService = new FileService(new NullLogService());
fileService.registerProvider(Schemas.userData, new UserDataFileSystemProvider(environmentService.userRoamingDataHome, new FileUserDataProvider(URI.file(parentDir), fileService))); fileService.registerProvider(Schemas.userData, new FileUserDataProvider(environmentService.appSettingsHome, dirname(environmentService.appSettingsHome), new DiskFileSystemProvider(new NullLogService())));
workspaceContextService = new WorkspaceService({ configurationCache: new ConfigurationCache(environmentService) }, environmentService, fileService, new RemoteAgentService(<IWindowConfiguration>{}, environmentService, new RemoteAuthorityResolverService(), new SignService())); workspaceContextService = new WorkspaceService({ configurationCache: new ConfigurationCache(environmentService) }, environmentService, fileService, new RemoteAgentService(<IWindowConfiguration>{}, environmentService, new RemoteAuthorityResolverService(), new SignService()));
return (<WorkspaceService>workspaceContextService).initialize(convertToWorkspacePayload(URI.file(folderDir))); return (<WorkspaceService>workspaceContextService).initialize(convertToWorkspacePayload(URI.file(folderDir)));
}); });
...@@ -158,12 +167,13 @@ suite('WorkspaceContextService - Workspace', () => { ...@@ -158,12 +167,13 @@ suite('WorkspaceContextService - Workspace', () => {
parentResource = parentDir; parentResource = parentDir;
instantiationService = <TestInstantiationService>workbenchInstantiationService(); instantiationService = <TestInstantiationService>workbenchInstantiationService();
const environmentService = new WorkbenchEnvironmentService(<IWindowConfiguration>parseArgs(process.argv), process.execPath); const environmentService = new TestEnvironmentService(URI.file(parentDir));
const remoteAgentService = instantiationService.createInstance(RemoteAgentService, {}); const remoteAgentService = instantiationService.createInstance(RemoteAgentService, {});
instantiationService.stub(IRemoteAgentService, remoteAgentService); instantiationService.stub(IRemoteAgentService, remoteAgentService);
const fileService = new FileService(new NullLogService()); const fileService = new FileService(new NullLogService());
fileService.registerProvider(Schemas.file, new DiskFileSystemProvider(new NullLogService())); const diskFileSystemProvider = new DiskFileSystemProvider(new NullLogService());
fileService.registerProvider(Schemas.userData, new UserDataFileSystemProvider(environmentService.userRoamingDataHome, new FileUserDataProvider(URI.file(parentDir), fileService))); fileService.registerProvider(Schemas.file, diskFileSystemProvider);
fileService.registerProvider(Schemas.userData, new FileUserDataProvider(environmentService.appSettingsHome, dirname(environmentService.appSettingsHome), diskFileSystemProvider));
const workspaceService = new WorkspaceService({ configurationCache: new ConfigurationCache(environmentService) }, environmentService, fileService, remoteAgentService); const workspaceService = new WorkspaceService({ configurationCache: new ConfigurationCache(environmentService) }, environmentService, fileService, remoteAgentService);
instantiationService.stub(IWorkspaceContextService, workspaceService); instantiationService.stub(IWorkspaceContextService, workspaceService);
...@@ -217,12 +227,13 @@ suite('WorkspaceContextService - Workspace Editing', () => { ...@@ -217,12 +227,13 @@ suite('WorkspaceContextService - Workspace Editing', () => {
parentResource = parentDir; parentResource = parentDir;
instantiationService = <TestInstantiationService>workbenchInstantiationService(); instantiationService = <TestInstantiationService>workbenchInstantiationService();
const environmentService = new WorkbenchEnvironmentService(<IWindowConfiguration>parseArgs(process.argv), process.execPath); const environmentService = new TestEnvironmentService(URI.file(parentDir));
const remoteAgentService = instantiationService.createInstance(RemoteAgentService, {}); const remoteAgentService = instantiationService.createInstance(RemoteAgentService, {});
instantiationService.stub(IRemoteAgentService, remoteAgentService); instantiationService.stub(IRemoteAgentService, remoteAgentService);
const fileService = new FileService(new NullLogService()); const fileService = new FileService(new NullLogService());
fileService.registerProvider(Schemas.file, new DiskFileSystemProvider(new NullLogService())); const diskFileSystemProvider = new DiskFileSystemProvider(new NullLogService());
fileService.registerProvider(Schemas.userData, new UserDataFileSystemProvider(environmentService.userRoamingDataHome, new FileUserDataProvider(URI.file(parentDir), fileService))); fileService.registerProvider(Schemas.file, diskFileSystemProvider);
fileService.registerProvider(Schemas.userData, new FileUserDataProvider(environmentService.appSettingsHome, dirname(environmentService.appSettingsHome), diskFileSystemProvider));
const workspaceService = new WorkspaceService({ configurationCache: new ConfigurationCache(environmentService) }, environmentService, fileService, remoteAgentService); const workspaceService = new WorkspaceService({ configurationCache: new ConfigurationCache(environmentService) }, environmentService, fileService, remoteAgentService);
instantiationService.stub(IWorkspaceContextService, workspaceService); instantiationService.stub(IWorkspaceContextService, workspaceService);
...@@ -477,12 +488,13 @@ suite('WorkspaceService - Initialization', () => { ...@@ -477,12 +488,13 @@ suite('WorkspaceService - Initialization', () => {
globalSettingsFile = path.join(parentDir, 'settings.json'); globalSettingsFile = path.join(parentDir, 'settings.json');
const instantiationService = <TestInstantiationService>workbenchInstantiationService(); const instantiationService = <TestInstantiationService>workbenchInstantiationService();
const environmentService = new WorkbenchEnvironmentService(<IWindowConfiguration>parseArgs(process.argv), process.execPath); const environmentService = new TestEnvironmentService(URI.file(parentDir));
const remoteAgentService = instantiationService.createInstance(RemoteAgentService, {}); const remoteAgentService = instantiationService.createInstance(RemoteAgentService, {});
instantiationService.stub(IRemoteAgentService, remoteAgentService); instantiationService.stub(IRemoteAgentService, remoteAgentService);
const fileService = new FileService(new NullLogService()); const fileService = new FileService(new NullLogService());
fileService.registerProvider(Schemas.file, new DiskFileSystemProvider(new NullLogService())); const diskFileSystemProvider = new DiskFileSystemProvider(new NullLogService());
fileService.registerProvider(Schemas.userData, new UserDataFileSystemProvider(environmentService.userRoamingDataHome, new FileUserDataProvider(URI.file(parentDir), fileService))); fileService.registerProvider(Schemas.file, diskFileSystemProvider);
fileService.registerProvider(Schemas.userData, new FileUserDataProvider(environmentService.appSettingsHome, dirname(environmentService.appSettingsHome), diskFileSystemProvider));
const workspaceService = new WorkspaceService({ configurationCache: new ConfigurationCache(environmentService) }, environmentService, fileService, remoteAgentService); const workspaceService = new WorkspaceService({ configurationCache: new ConfigurationCache(environmentService) }, environmentService, fileService, remoteAgentService);
instantiationService.stub(IWorkspaceContextService, workspaceService); instantiationService.stub(IWorkspaceContextService, workspaceService);
instantiationService.stub(IConfigurationService, workspaceService); instantiationService.stub(IConfigurationService, workspaceService);
...@@ -740,12 +752,13 @@ suite('WorkspaceConfigurationService - Folder', () => { ...@@ -740,12 +752,13 @@ suite('WorkspaceConfigurationService - Folder', () => {
globalSettingsFile = path.join(parentDir, 'settings.json'); globalSettingsFile = path.join(parentDir, 'settings.json');
const instantiationService = <TestInstantiationService>workbenchInstantiationService(); const instantiationService = <TestInstantiationService>workbenchInstantiationService();
const environmentService = new WorkbenchEnvironmentService(<IWindowConfiguration>parseArgs(process.argv), process.execPath); const environmentService = new TestEnvironmentService(URI.file(parentDir));
const remoteAgentService = instantiationService.createInstance(RemoteAgentService, {}); const remoteAgentService = instantiationService.createInstance(RemoteAgentService, {});
instantiationService.stub(IRemoteAgentService, remoteAgentService); instantiationService.stub(IRemoteAgentService, remoteAgentService);
const fileService = new FileService(new NullLogService()); const fileService = new FileService(new NullLogService());
fileService.registerProvider(Schemas.file, new DiskFileSystemProvider(new NullLogService())); const diskFileSystemProvider = new DiskFileSystemProvider(new NullLogService());
fileService.registerProvider(Schemas.userData, new UserDataFileSystemProvider(environmentService.userRoamingDataHome, new FileUserDataProvider(URI.file(parentDir), fileService))); fileService.registerProvider(Schemas.file, diskFileSystemProvider);
fileService.registerProvider(Schemas.userData, new FileUserDataProvider(environmentService.appSettingsHome, dirname(environmentService.appSettingsHome), diskFileSystemProvider));
const workspaceService = new WorkspaceService({ configurationCache: new ConfigurationCache(environmentService) }, environmentService, fileService, remoteAgentService); const workspaceService = new WorkspaceService({ configurationCache: new ConfigurationCache(environmentService) }, environmentService, fileService, remoteAgentService);
instantiationService.stub(IWorkspaceContextService, workspaceService); instantiationService.stub(IWorkspaceContextService, workspaceService);
instantiationService.stub(IConfigurationService, workspaceService); instantiationService.stub(IConfigurationService, workspaceService);
...@@ -1069,12 +1082,13 @@ suite('WorkspaceConfigurationService-Multiroot', () => { ...@@ -1069,12 +1082,13 @@ suite('WorkspaceConfigurationService-Multiroot', () => {
globalSettingsFile = path.join(parentDir, 'settings.json'); globalSettingsFile = path.join(parentDir, 'settings.json');
const instantiationService = <TestInstantiationService>workbenchInstantiationService(); const instantiationService = <TestInstantiationService>workbenchInstantiationService();
const environmentService = new WorkbenchEnvironmentService(<IWindowConfiguration>parseArgs(process.argv), process.execPath); const environmentService = new TestEnvironmentService(URI.file(parentDir));
const remoteAgentService = instantiationService.createInstance(RemoteAgentService, {}); const remoteAgentService = instantiationService.createInstance(RemoteAgentService, {});
instantiationService.stub(IRemoteAgentService, remoteAgentService); instantiationService.stub(IRemoteAgentService, remoteAgentService);
const fileService = new FileService(new NullLogService()); const fileService = new FileService(new NullLogService());
fileService.registerProvider(Schemas.file, new DiskFileSystemProvider(new NullLogService())); const diskFileSystemProvider = new DiskFileSystemProvider(new NullLogService());
fileService.registerProvider(Schemas.userData, new UserDataFileSystemProvider(environmentService.userRoamingDataHome, new FileUserDataProvider(URI.file(parentDir), fileService))); fileService.registerProvider(Schemas.file, diskFileSystemProvider);
fileService.registerProvider(Schemas.userData, new FileUserDataProvider(environmentService.appSettingsHome, dirname(environmentService.appSettingsHome), diskFileSystemProvider));
const workspaceService = new WorkspaceService({ configurationCache: new ConfigurationCache(environmentService) }, environmentService, fileService, remoteAgentService); const workspaceService = new WorkspaceService({ configurationCache: new ConfigurationCache(environmentService) }, environmentService, fileService, remoteAgentService);
instantiationService.stub(IWorkspaceContextService, workspaceService); instantiationService.stub(IWorkspaceContextService, workspaceService);
...@@ -1471,12 +1485,12 @@ suite('WorkspaceConfigurationService - Remote Folder', () => { ...@@ -1471,12 +1485,12 @@ suite('WorkspaceConfigurationService - Remote Folder', () => {
remoteSettingsFile = path.join(parentDir, 'remote-settings.json'); remoteSettingsFile = path.join(parentDir, 'remote-settings.json');
instantiationService = <TestInstantiationService>workbenchInstantiationService(); instantiationService = <TestInstantiationService>workbenchInstantiationService();
const environmentService = new WorkbenchEnvironmentService(<IWindowConfiguration>parseArgs(process.argv), process.execPath); const environmentService = new TestEnvironmentService(URI.file(parentDir));
const remoteEnvironmentPromise = new Promise<Partial<IRemoteAgentEnvironment>>(c => resolveRemoteEnvironment = () => c({ settingsPath: URI.file(remoteSettingsFile).with({ scheme: Schemas.vscodeRemote, authority: remoteAuthority }) })); const remoteEnvironmentPromise = new Promise<Partial<IRemoteAgentEnvironment>>(c => resolveRemoteEnvironment = () => c({ settingsPath: URI.file(remoteSettingsFile).with({ scheme: Schemas.vscodeRemote, authority: remoteAuthority }) }));
const remoteAgentService = instantiationService.stub(IRemoteAgentService, <Partial<IRemoteAgentService>>{ getEnvironment: () => remoteEnvironmentPromise }); const remoteAgentService = instantiationService.stub(IRemoteAgentService, <Partial<IRemoteAgentService>>{ getEnvironment: () => remoteEnvironmentPromise });
const fileService = new FileService(new NullLogService()); const fileService = new FileService(new NullLogService());
fileService.registerProvider(Schemas.file, diskFileSystemProvider); fileService.registerProvider(Schemas.file, diskFileSystemProvider);
fileService.registerProvider(Schemas.userData, new UserDataFileSystemProvider(environmentService.userRoamingDataHome, new FileUserDataProvider(URI.file(parentDir), fileService))); fileService.registerProvider(Schemas.userData, new FileUserDataProvider(environmentService.appSettingsHome, dirname(environmentService.appSettingsHome), diskFileSystemProvider));
const configurationCache: IConfigurationCache = { read: () => Promise.resolve(''), write: () => Promise.resolve(), remove: () => Promise.resolve() }; const configurationCache: IConfigurationCache = { read: () => Promise.resolve(''), write: () => Promise.resolve(), remove: () => Promise.resolve() };
testObject = new WorkspaceService({ configurationCache, remoteAuthority }, environmentService, fileService, remoteAgentService); testObject = new WorkspaceService({ configurationCache, remoteAuthority }, environmentService, fileService, remoteAgentService);
instantiationService.stub(IWorkspaceContextService, testObject); instantiationService.stub(IWorkspaceContextService, testObject);
......
...@@ -45,11 +45,21 @@ import { FileService } from 'vs/workbench/services/files/common/fileService'; ...@@ -45,11 +45,21 @@ import { FileService } from 'vs/workbench/services/files/common/fileService';
import { Schemas } from 'vs/base/common/network'; import { Schemas } from 'vs/base/common/network';
import { DiskFileSystemProvider } from 'vs/workbench/services/files/node/diskFileSystemProvider'; import { DiskFileSystemProvider } from 'vs/workbench/services/files/node/diskFileSystemProvider';
import { URI } from 'vs/base/common/uri'; import { URI } from 'vs/base/common/uri';
import { UserDataFileSystemProvider } from 'vs/workbench/services/userData/common/userDataFileSystemProvider';
import { FileUserDataProvider } from 'vs/workbench/services/userData/common/fileUserDataProvider'; import { FileUserDataProvider } from 'vs/workbench/services/userData/common/fileUserDataProvider';
import { parseArgs } from 'vs/platform/environment/node/argv'; import { parseArgs } from 'vs/platform/environment/node/argv';
import { WorkbenchEnvironmentService } from 'vs/workbench/services/environment/node/environmentService'; import { WorkbenchEnvironmentService } from 'vs/workbench/services/environment/node/environmentService';
import { IWindowConfiguration } from 'vs/platform/windows/common/windows'; import { IWindowConfiguration } from 'vs/platform/windows/common/windows';
import { dirname } from 'vs/base/common/resources';
class TestBackupEnvironmentService extends WorkbenchEnvironmentService {
constructor(private _appSettingsHome: URI) {
super(parseArgs(process.argv) as IWindowConfiguration, process.execPath);
}
get appSettingsHome() { return this._appSettingsHome; }
}
interface Modifiers { interface Modifiers {
metaKey?: boolean; metaKey?: boolean;
...@@ -71,7 +81,7 @@ suite('KeybindingsEditing', () => { ...@@ -71,7 +81,7 @@ suite('KeybindingsEditing', () => {
instantiationService = new TestInstantiationService(); instantiationService = new TestInstantiationService();
const environmentService = new WorkbenchEnvironmentService(<IWindowConfiguration>parseArgs(process.argv), process.execPath); const environmentService = new TestBackupEnvironmentService(URI.file(testDir));
instantiationService.stub(IEnvironmentService, environmentService); instantiationService.stub(IEnvironmentService, environmentService);
instantiationService.stub(IConfigurationService, ConfigurationService); instantiationService.stub(IConfigurationService, ConfigurationService);
instantiationService.stub(IConfigurationService, 'getValue', { 'eol': '\n' }); instantiationService.stub(IConfigurationService, 'getValue', { 'eol': '\n' });
...@@ -89,8 +99,9 @@ suite('KeybindingsEditing', () => { ...@@ -89,8 +99,9 @@ suite('KeybindingsEditing', () => {
instantiationService.stub(ITextResourcePropertiesService, new TestTextResourcePropertiesService(instantiationService.get(IConfigurationService))); instantiationService.stub(ITextResourcePropertiesService, new TestTextResourcePropertiesService(instantiationService.get(IConfigurationService)));
instantiationService.stub(IModelService, instantiationService.createInstance(ModelServiceImpl)); instantiationService.stub(IModelService, instantiationService.createInstance(ModelServiceImpl));
const fileService = new FileService(new NullLogService()); const fileService = new FileService(new NullLogService());
fileService.registerProvider(Schemas.file, new DiskFileSystemProvider(new NullLogService())); const diskFileSystemProvider = new DiskFileSystemProvider(new NullLogService());
fileService.registerProvider(Schemas.userData, new UserDataFileSystemProvider(environmentService.userRoamingDataHome, new FileUserDataProvider(URI.file(testDir), fileService))); fileService.registerProvider(Schemas.file, diskFileSystemProvider);
fileService.registerProvider(Schemas.userData, new FileUserDataProvider(environmentService.appSettingsHome, dirname(environmentService.appSettingsHome), diskFileSystemProvider));
instantiationService.stub(IFileService, fileService); instantiationService.stub(IFileService, fileService);
instantiationService.stub(IUntitledEditorService, instantiationService.createInstance(UntitledEditorService)); instantiationService.stub(IUntitledEditorService, instantiationService.createInstance(UntitledEditorService));
instantiationService.stub(ITextFileService, instantiationService.createInstance(TestTextFileService)); instantiationService.stub(ITextFileService, instantiationService.createInstance(TestTextFileService));
......
...@@ -4,120 +4,134 @@ ...@@ -4,120 +4,134 @@
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
import { Event, Emitter } from 'vs/base/common/event'; import { Event, Emitter } from 'vs/base/common/event';
import { Disposable } from 'vs/base/common/lifecycle'; import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
import { IUserDataProvider, FileChangeEvent, IUserDataContainerRegistry, Extensions } from 'vs/workbench/services/userData/common/userData'; import { IFileSystemProviderWithFileReadWriteCapability, IFileChange, IWatchOptions, IStat, FileOverwriteOptions, FileType, FileWriteOptions, FileDeleteOptions, FileSystemProviderCapabilities, IFileSystemProviderWithOpenReadWriteCloseCapability, FileOpenOptions, hasReadWriteCapability, hasOpenReadWriteCloseCapability } from 'vs/platform/files/common/files';
import { IFileService, FileChangesEvent, FileChangeType } from 'vs/platform/files/common/files';
import { URI } from 'vs/base/common/uri'; import { URI } from 'vs/base/common/uri';
import * as resources from 'vs/base/common/resources'; import * as resources from 'vs/base/common/resources';
import { VSBuffer } from 'vs/base/common/buffer';
import { startsWith } from 'vs/base/common/strings'; import { startsWith } from 'vs/base/common/strings';
import { BACKUPS } from 'vs/platform/environment/common/environment'; import { BACKUPS } from 'vs/platform/environment/common/environment';
import { Registry } from 'vs/platform/registry/common/platform'; import { Schemas } from 'vs/base/common/network';
export class FileUserDataProvider extends Disposable implements IUserDataProvider { export class FileUserDataProvider extends Disposable implements IFileSystemProviderWithFileReadWriteCapability, IFileSystemProviderWithOpenReadWriteCloseCapability {
private _onDidChangeFile: Emitter<FileChangeEvent[]> = this._register(new Emitter<FileChangeEvent[]>()); readonly capabilities: FileSystemProviderCapabilities = this.fileSystemProvider.capabilities;
readonly onDidChangeFile: Event<FileChangeEvent[]> = this._onDidChangeFile.event; readonly onDidChangeCapabilities: Event<void> = Event.None;
private readonly _onDidChangeFile: Emitter<IFileChange[]> = this._register(new Emitter<IFileChange[]>());
readonly onDidChangeFile: Event<IFileChange[]> = this._onDidChangeFile.event;
constructor( constructor(
private readonly userDataHome: URI, private readonly userDataHome: URI,
@IFileService private readonly fileService: IFileService private readonly backupsHome: URI,
private readonly fileSystemProvider: IFileSystemProviderWithFileReadWriteCapability | IFileSystemProviderWithOpenReadWriteCloseCapability,
) { ) {
super(); super();
// Assumption: This path always exists // Assumption: This path always exists
this._register(this.fileService.watch(this.userDataHome)); this._register(this.fileSystemProvider.watch(this.userDataHome, { recursive: false, excludes: [] }));
this._register(this.fileService.onFileChanges(e => this.handleFileChanges(e))); this._register(this.fileSystemProvider.onDidChangeFile(e => this.handleFileChanges(e)));
}
const userDataContainersRegistry = Registry.as<IUserDataContainerRegistry>(Extensions.UserDataContainers);
userDataContainersRegistry.containers.forEach(c => this.watchContainer(c)); watch(resource: URI, opts: IWatchOptions): IDisposable {
this._register(userDataContainersRegistry.onDidRegisterContainer(c => this.watchContainer(c))); return this.fileSystemProvider.watch(this.toFileSystemResource(resource), opts);
} }
private handleFileChanges(event: FileChangesEvent): void { stat(resource: URI): Promise<IStat> {
const changedPaths: FileChangeEvent[] = []; return this.fileSystemProvider.stat(this.toFileSystemResource(resource));
const userDataContainersRegistry = Registry.as<IUserDataContainerRegistry>(Extensions.UserDataContainers); }
for (const change of event.changes) {
if (change.resource.scheme === this.userDataHome.scheme) { mkdir(resource: URI): Promise<void> {
const path = this.toPath(change.resource); return this.fileSystemProvider.mkdir(this.toFileSystemResource(resource));
if (path) { }
changedPaths.push({
path, rename(from: URI, to: URI, opts: FileOverwriteOptions): Promise<void> {
type: change.type return this.fileSystemProvider.rename(this.toFileSystemResource(from), this.toFileSystemResource(to), opts);
}); }
if (userDataContainersRegistry.isContainer(path)) {
if (change.type === FileChangeType.ADDED) { readFile(resource: URI): Promise<Uint8Array> {
this.watchContainer(path); if (hasReadWriteCapability(this.fileSystemProvider)) {
} return this.fileSystemProvider.readFile(this.toFileSystemResource(resource));
}
}
}
}
if (changedPaths.length) {
this._onDidChangeFile.fire(changedPaths);
} }
throw new Error('not supported');
}
readdir(resource: URI): Promise<[string, FileType][]> {
return this.fileSystemProvider.readdir(this.toFileSystemResource(resource));
} }
private async watchContainer(container: string): Promise<void> { writeFile(resource: URI, content: Uint8Array, opts: FileWriteOptions): Promise<void> {
if (this.isBackUpsPath(container)) { if (hasReadWriteCapability(this.fileSystemProvider)) {
return; return this.fileSystemProvider.writeFile(this.toFileSystemResource(resource), content, opts);
} }
const resource = this.toResource(container); throw new Error('not supported');
const exists = await this.fileService.exists(resource); }
if (exists) {
this._register(this.fileService.watch(resource)); open(resource: URI, opts: FileOpenOptions): Promise<number> {
if (hasOpenReadWriteCloseCapability(this.fileSystemProvider)) {
return this.fileSystemProvider.open(this.toFileSystemResource(resource), opts);
} }
throw new Error('not supported');
} }
async readFile(path: string): Promise<Uint8Array> { close(fd: number): Promise<void> {
const resource = this.toResource(path); if (hasOpenReadWriteCloseCapability(this.fileSystemProvider)) {
const content = await this.fileService.readFile(resource); return this.fileSystemProvider.close(fd);
return content.value.buffer; }
throw new Error('not supported');
} }
writeFile(path: string, value: Uint8Array): Promise<void> { read(fd: number, pos: number, data: Uint8Array, offset: number, length: number): Promise<number> {
return this.fileService.writeFile(this.toResource(path), VSBuffer.wrap(value)).then(() => undefined); if (hasOpenReadWriteCloseCapability(this.fileSystemProvider)) {
return this.fileSystemProvider.read(fd, pos, data, offset, length);
}
throw new Error('not supported');
} }
async listFiles(path: string): Promise<string[]> { write(fd: number, pos: number, data: Uint8Array, offset: number, length: number): Promise<number> {
const resource = this.toResource(path); if (hasOpenReadWriteCloseCapability(this.fileSystemProvider)) {
try { return this.fileSystemProvider.write(fd, pos, data, offset, length);
const result = await this.fileService.resolve(resource);
if (result.children) {
return result.children
.filter(c => !c.isDirectory)
.map(c => this.toRelativePath(c.resource, resource)!);
}
} catch (error) {
} }
return []; throw new Error('not supported');
} }
deleteFile(path: string): Promise<void> { delete(resource: URI, opts: FileDeleteOptions): Promise<void> {
return this.fileService.del(this.toResource(path)); return this.fileSystemProvider.delete(this.toFileSystemResource(resource), opts);
} }
private toResource(path: string): URI { private handleFileChanges(changes: IFileChange[]): void {
if (this.isBackUpsPath(path)) { const userDataChanges: IFileChange[] = [];
return resources.joinPath(resources.dirname(this.userDataHome), path); for (const change of changes) {
const userDataResource = this.toUserDataResource(change.resource);
if (userDataResource) {
userDataChanges.push({
resource: userDataResource,
type: change.type
});
}
}
if (userDataChanges.length) {
this._onDidChangeFile.fire(userDataChanges);
} }
return resources.joinPath(this.userDataHome, path);
} }
private isBackUpsPath(path: string): boolean { private toFileSystemResource(userDataResource: URI): URI {
return path === BACKUPS || startsWith(path, `${BACKUPS}/`); const fileSystemResource = userDataResource.with({ scheme: this.userDataHome.scheme });
const relativePath = resources.relativePath(this.userDataHome, fileSystemResource);
if (relativePath && startsWith(relativePath, BACKUPS)) {
return resources.joinPath(resources.dirname(this.backupsHome), relativePath);
}
return fileSystemResource;
} }
private toPath(resource: URI): string | undefined { private toUserDataResource(fileSystemResource: URI): URI | null {
let result = this.toRelativePath(resource, this.userDataHome); if (resources.relativePath(this.userDataHome, fileSystemResource)) {
if (result === undefined) { return fileSystemResource.with({ scheme: Schemas.userData });
result = this.toRelativePath(resource, resources.joinPath(resources.dirname(this.userDataHome), BACKUPS)); }
const relativePath = resources.relativePath(this.backupsHome, fileSystemResource);
if (relativePath) {
return resources.joinPath(this.userDataHome, BACKUPS, relativePath).with({ scheme: Schemas.userData });
} }
return result; return null;
} }
private toRelativePath(fromResource: URI, toResource: URI): string | undefined {
const fromPath = fromResource.toString();
const toPath = toResource.toString();
return startsWith(fromPath, toPath) ? fromPath.substr(toPath.length + 1) : undefined;
}
} }
\ No newline at end of file
...@@ -3,9 +3,7 @@ ...@@ -3,9 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information. * Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
import { Event, Emitter } from 'vs/base/common/event'; import { Event } from 'vs/base/common/event';
import { TernarySearchTree } from 'vs/base/common/map';
import { Registry } from 'vs/platform/registry/common/platform';
import { FileChangeType } from 'vs/platform/files/common/files'; import { FileChangeType } from 'vs/platform/files/common/files';
/** /**
...@@ -80,59 +78,3 @@ export interface IUserDataProvider { ...@@ -80,59 +78,3 @@ export interface IUserDataProvider {
*/ */
listFiles(path: string): Promise<string[]>; listFiles(path: string): Promise<string[]>;
} }
export interface IUserDataContainerRegistry {
/**
* An event to signal that a container has been registered.
*/
readonly onDidRegisterContainer: Event<string>;
/**
* Registered containers
*/
readonly containers: string[];
/**
* Register the given path as an user data container if user data files are stored under this path.
*
* It is required to register the container to access the user data files under the container.
*/
registerContainer(path: string): void;
/**
* Returns true if the given path is an user data container or sub container of user data container
*/
isContainer(path: string): boolean;
}
class UserDataContainerRegistry implements IUserDataContainerRegistry {
private _containers: TernarySearchTree<string> = TernarySearchTree.forStrings();
private _onDidRegisterContainer: Emitter<string> = new Emitter<string>();
readonly onDidRegisterContainer: Event<string> = this._onDidRegisterContainer.event;
get containers(): string[] {
const containers: string[] = [];
this._containers.forEach(c => containers.push(c));
return containers;
}
public registerContainer(path: string): void {
if (!this._containers.get(path)) {
this._containers.set(path, path);
this._onDidRegisterContainer.fire(path);
}
}
isContainer(path: string): boolean {
return !!this._containers.get(path) || !!this._containers.findSuperstr(path);
}
}
export const Extensions = {
UserDataContainers: 'workbench.contributions.userDataContainers'
};
Registry.add(Extensions.UserDataContainers, new UserDataContainerRegistry());
\ No newline at end of file
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IDisposable, Disposable } from 'vs/base/common/lifecycle';
import { FileSystemProviderCapabilities, FileWriteOptions, IStat, FileType, FileDeleteOptions, IWatchOptions, FileOverwriteOptions, IFileSystemProviderWithFileReadWriteCapability, IFileChange, FileChangeType, FileChangesEvent } from 'vs/platform/files/common/files';
import { IUserDataProvider, IUserDataContainerRegistry, Extensions, FileChangeEvent } from 'vs/workbench/services/userData/common/userData';
import { URI } from 'vs/base/common/uri';
import { Event, Emitter } from 'vs/base/common/event';
import { startsWith } from 'vs/base/common/strings';
import { Registry } from 'vs/platform/registry/common/platform';
import { joinPath } from 'vs/base/common/resources';
export class UserDataFileSystemProvider extends Disposable implements IFileSystemProviderWithFileReadWriteCapability {
private readonly versions: Map<string, number> = new Map<string, number>();
readonly capabilities: FileSystemProviderCapabilities = FileSystemProviderCapabilities.FileReadWrite;
readonly onDidChangeCapabilities: Event<void> = Event.None;
private readonly _onDidChangeFile: Emitter<IFileChange[]> = this._register(new Emitter<IFileChange[]>());
readonly onDidChangeFile: Event<IFileChange[]> = this._onDidChangeFile.event;
constructor(
private readonly userDataHome: URI,
private readonly userDataProvider: IUserDataProvider
) {
super();
this._register(this.userDataProvider.onDidChangeFile(changes => this.onDidChangeUserData(changes)));
}
private onDidChangeUserData(changes: FileChangeEvent[]): void {
const fileChanges: IFileChange[] = [];
for (const { path, type } of changes) {
if (type === FileChangeType.DELETED) {
this.versions.delete(path);
} else {
this.versions.set(path, this.getOrSetVersion(path) + 1);
}
fileChanges.push({
resource: this.toResource(path),
type
});
}
if (fileChanges.length) {
this._onDidChangeFile.fire(new FileChangesEvent(fileChanges).changes);
}
}
watch(resource: URI, opts: IWatchOptions): IDisposable {
const path = this.toPath(resource);
if (!path) {
throw new Error(`Invalid user data resource ${resource}`);
}
return Disposable.None;
}
async stat(resource: URI): Promise<IStat> {
const path = this.toPath(resource);
if (path === undefined) {
throw new Error(`Invalid user data resource ${resource}`);
}
if (this.isContainer(path)) {
return {
type: FileType.Directory,
ctime: 0,
mtime: this.getOrSetVersion(path),
size: 0
};
}
const result = await this.readFile(resource);
return {
type: FileType.File,
ctime: 0,
mtime: this.getOrSetVersion(path),
size: result.byteLength
};
}
mkdir(resource: URI): Promise<void> { throw new Error('not supported'); }
rename(from: URI, to: URI, opts: FileOverwriteOptions): Promise<void> { throw new Error('not supported'); }
async readFile(resource: URI): Promise<Uint8Array> {
const path = this.toPath(resource);
if (!path) {
throw new Error(`Invalid user data resource ${resource}`);
}
if (this.isContainer(path)) {
throw new Error(`Invalid user data file ${resource}`);
}
return this.userDataProvider.readFile(path);
}
async readdir(resource: URI): Promise<[string, FileType][]> {
const path = this.toPath(resource);
if (!path) {
throw new Error(`Invalid user data resource ${resource}`);
}
if (!this.isContainer(path)) {
throw new Error(`Invalid user data container ${resource}`);
}
const children = await this.userDataProvider.listFiles(path);
return children.map(c => [c, FileType.File]);
}
writeFile(resource: URI, content: Uint8Array, opts: FileWriteOptions): Promise<void> {
const path = this.toPath(resource);
if (!path) {
throw new Error(`Invalid user data resource ${resource}`);
}
if (this.isContainer(path)) {
throw new Error(`Invalid user data file ${resource}`);
}
return this.userDataProvider.writeFile(path, content);
}
delete(resource: URI, opts: FileDeleteOptions): Promise<void> {
const path = this.toPath(resource);
if (!path) {
throw new Error(`Invalid user data resource ${resource}`);
}
if (this.isContainer(path)) {
throw new Error(`Invalid user data file ${resource}`);
}
return this.userDataProvider.deleteFile(path);
}
private getOrSetVersion(path: string): number {
if (!this.versions.has(path)) {
this.versions.set(path, 1);
}
return this.versions.get(path)!;
}
private isContainer(path: string): boolean {
if (path === '') {
return true; // Root
}
return Registry.as<IUserDataContainerRegistry>(Extensions.UserDataContainers).isContainer(path);
}
private toResource(path: string): URI {
return joinPath(this.userDataHome, path);
}
private toPath(resource: URI): string | undefined {
return this.toRelativePath(resource, this.userDataHome);
}
private toRelativePath(fromResource: URI, toResource: URI): string | undefined {
const fromPath = fromResource.toString();
const toPath = toResource.toString();
return startsWith(fromPath, toPath) ? fromPath.substr(toPath.length + 1) : undefined;
}
}
\ No newline at end of file
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
import 'vs/workbench/workbench.web.main'; import 'vs/workbench/workbench.web.main';
import { main } from 'vs/workbench/browser/web.main'; import { main } from 'vs/workbench/browser/web.main';
import { UriComponents } from 'vs/base/common/uri'; import { UriComponents } from 'vs/base/common/uri';
import { IUserDataProvider } from 'vs/workbench/services/userData/common/userData'; import { IFileSystemProviderWithFileReadWriteCapability, IFileSystemProviderWithOpenReadWriteCloseCapability } from 'vs/platform/files/common/files';
export interface IWorkbenchConstructionOptions { export interface IWorkbenchConstructionOptions {
...@@ -36,7 +36,7 @@ export interface IWorkbenchConstructionOptions { ...@@ -36,7 +36,7 @@ export interface IWorkbenchConstructionOptions {
* Experimental: The userDataProvider is used to handle user specific application * Experimental: The userDataProvider is used to handle user specific application
* state like settings, keybindings, UI state (e.g. opened editors) and snippets. * state like settings, keybindings, UI state (e.g. opened editors) and snippets.
*/ */
userDataProvider?: IUserDataProvider; userDataProvider?: IFileSystemProviderWithFileReadWriteCapability | IFileSystemProviderWithOpenReadWriteCloseCapability;
} }
/** /**
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册