From f1150d66fcff3c56bc6fc68b9fa7d7a88e19859b Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Sun, 23 Jun 2019 17:26:53 +0200 Subject: [PATCH] UserDataFileProvider for handling user data resources --- src/vs/base/common/network.ts | 2 + src/vs/base/test/node/utils.ts | 4 +- .../sharedProcess/sharedProcessMain.ts | 2 +- src/vs/code/electron-main/main.ts | 2 +- src/vs/code/node/cliProcessMain.ts | 2 +- .../standalone/browser/simpleServices.ts | 2 + .../configuration/common/configuration.ts | 1 + .../node/configurationService.ts | 14 ++-- .../test/common/testConfigurationService.ts | 2 + .../test/node/configurationService.test.ts | 39 +++++----- .../environment/common/environment.ts | 1 - .../environment/node/environmentService.ts | 3 - .../electron-browser/telemetryService.test.ts | 2 + .../windows/electron-main/windowsService.ts | 3 + src/vs/workbench/browser/web.main.ts | 6 +- .../files/browser/fileActions.contribution.ts | 1 - .../browser/preferences.contribution.ts | 4 +- .../common/preferencesContribution.ts | 4 +- .../electron-browser/main.contribution.ts | 14 ++++ src/vs/workbench/electron-browser/main.ts | 2 + .../configuration/browser/configuration.ts | 5 +- .../browser/configurationService.ts | 2 + .../common/configurationEditingService.ts | 3 +- .../configurationEditingService.test.ts | 3 +- .../configurationService.test.ts | 27 +++---- .../configurationResolverService.test.ts | 3 +- .../environment/browser/environmentService.ts | 2 - .../keybindingEditing.test.ts | 2 +- .../preferences/browser/preferencesService.ts | 4 +- .../textfile/common/textFileEditorModel.ts | 6 +- .../common/textResourcePropertiesService.ts | 2 +- .../userData/common/fileUserDataService.ts | 23 ++++-- .../userData/common/userDataFileProvider.ts | 72 +++++++++++++++++++ .../userData/common/userDataService.ts | 6 ++ 34 files changed, 199 insertions(+), 71 deletions(-) create mode 100644 src/vs/workbench/services/userData/common/userDataFileProvider.ts diff --git a/src/vs/base/common/network.ts b/src/vs/base/common/network.ts index a7466e641af..46d2933a05e 100644 --- a/src/vs/base/common/network.ts +++ b/src/vs/base/common/network.ts @@ -46,4 +46,6 @@ export namespace Schemas { export const command: string = 'command'; export const vscodeRemote: string = 'vscode-remote'; + + export const userData: string = 'vscode-userdata'; } diff --git a/src/vs/base/test/node/utils.ts b/src/vs/base/test/node/utils.ts index 58e77924fe6..5ba6f6e2472 100644 --- a/src/vs/base/test/node/utils.ts +++ b/src/vs/base/test/node/utils.ts @@ -16,8 +16,8 @@ export interface ITestFileResult { export function testFile(folder: string, file: string): Promise { const id = generateUuid(); const parentDir = join(tmpdir(), 'vsctests', id); - const newDir = join(parentDir, 'config', id); - const testFile = join(newDir, 'config.json'); + const newDir = join(parentDir, folder, id); + const testFile = join(newDir, file); return mkdirp(newDir, 493).then(() => { return { diff --git a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts index 34cd3e48a63..da1e7be6953 100644 --- a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts +++ b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts @@ -98,7 +98,7 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat disposables.push(logService); logService.info('main', JSON.stringify(configuration)); - const configurationService = new ConfigurationService(environmentService.settingsResource); + const configurationService = new ConfigurationService(environmentService.appSettingsHome); disposables.push(configurationService); await configurationService.initialize(); diff --git a/src/vs/code/electron-main/main.ts b/src/vs/code/electron-main/main.ts index be40bfa20d0..d1be121aad1 100644 --- a/src/vs/code/electron-main/main.ts +++ b/src/vs/code/electron-main/main.ts @@ -142,7 +142,7 @@ class CodeMain { process.once('exit', () => logService.dispose()); services.set(ILogService, logService); - services.set(IConfigurationService, new ConfigurationService(environmentService.settingsResource)); + services.set(IConfigurationService, new ConfigurationService(environmentService.appSettingsHome)); services.set(ILifecycleService, new SyncDescriptor(LifecycleService)); services.set(IStateService, new SyncDescriptor(StateService)); services.set(IRequestService, new SyncDescriptor(RequestService)); diff --git a/src/vs/code/node/cliProcessMain.ts b/src/vs/code/node/cliProcessMain.ts index dafebd1c91f..63d6cfccca2 100644 --- a/src/vs/code/node/cliProcessMain.ts +++ b/src/vs/code/node/cliProcessMain.ts @@ -285,7 +285,7 @@ export async function main(argv: ParsedArgs): Promise { await Promise.all([environmentService.appSettingsHome.fsPath, environmentService.extensionsPath].map(p => mkdirp(p))); - const configurationService = new ConfigurationService(environmentService.settingsResource); + const configurationService = new ConfigurationService(environmentService.appSettingsHome); await configurationService.initialize(); services.set(IEnvironmentService, environmentService); diff --git a/src/vs/editor/standalone/browser/simpleServices.ts b/src/vs/editor/standalone/browser/simpleServices.ts index 1f44f246486..9a5ee6ed071 100644 --- a/src/vs/editor/standalone/browser/simpleServices.ts +++ b/src/vs/editor/standalone/browser/simpleServices.ts @@ -413,6 +413,8 @@ export class SimpleConfigurationService implements IConfigurationService { private readonly _configuration: Configuration; + userSettingsResource = URI.file('settings.json'); + constructor() { this._configuration = new Configuration(new DefaultConfigurationModel(), new ConfigurationModel()); } diff --git a/src/vs/platform/configuration/common/configuration.ts b/src/vs/platform/configuration/common/configuration.ts index e9758098d43..d98ce059c77 100644 --- a/src/vs/platform/configuration/common/configuration.ts +++ b/src/vs/platform/configuration/common/configuration.ts @@ -65,6 +65,7 @@ export interface IConfigurationChangeEvent { export interface IConfigurationService { _serviceBrand: any; + userSettingsResource: URI; onDidChangeConfiguration: Event; getConfigurationData(): IConfigurationData | null; diff --git a/src/vs/platform/configuration/node/configurationService.ts b/src/vs/platform/configuration/node/configurationService.ts index d3e4dfe09b1..aa8502dec8f 100644 --- a/src/vs/platform/configuration/node/configurationService.ts +++ b/src/vs/platform/configuration/node/configurationService.ts @@ -14,11 +14,14 @@ import { ConfigWatcher } from 'vs/base/node/config'; import { onUnexpectedError } from 'vs/base/common/errors'; import { URI } from 'vs/base/common/uri'; import { Schemas } from 'vs/base/common/network'; +import { joinPath } from 'vs/base/common/resources'; export class ConfigurationService extends Disposable implements IConfigurationService, IDisposable { _serviceBrand: any; + readonly userSettingsResource: URI; + private configuration: Configuration; private userConfigModelWatcher: ConfigWatcher | undefined; @@ -26,9 +29,10 @@ export class ConfigurationService extends Disposable implements IConfigurationSe readonly onDidChangeConfiguration: Event = this._onDidChangeConfiguration.event; constructor( - private readonly settingsResource: URI + appSettingsHome: URI ) { super(); + this.userSettingsResource = joinPath(appSettingsHome, 'settings.json'); this.configuration = new Configuration(new DefaultConfigurationModel(), new ConfigurationModel()); this._register(Registry.as(Extensions.Configuration).onDidUpdateConfiguration(configurationProperties => this.onDidDefaultConfigurationChange(configurationProperties))); } @@ -38,13 +42,13 @@ export class ConfigurationService extends Disposable implements IConfigurationSe this.userConfigModelWatcher.dispose(); } - if (this.settingsResource.scheme !== Schemas.file) { + if (this.userSettingsResource.scheme !== Schemas.file) { return Promise.resolve(); } return new Promise((c, e) => { - this.userConfigModelWatcher = this._register(new ConfigWatcher(this.settingsResource.fsPath, { - changeBufferDelay: 300, onError: error => onUnexpectedError(error), defaultConfig: new ConfigurationModelParser(this.settingsResource.fsPath), parse: (content: string, parseErrors: any[]) => { - const userConfigModelParser = new ConfigurationModelParser(this.settingsResource.fsPath); + this.userConfigModelWatcher = this._register(new ConfigWatcher(this.userSettingsResource.fsPath, { + changeBufferDelay: 300, onError: error => onUnexpectedError(error), defaultConfig: new ConfigurationModelParser(this.userSettingsResource.fsPath), parse: (content: string, parseErrors: any[]) => { + const userConfigModelParser = new ConfigurationModelParser(this.userSettingsResource.fsPath); userConfigModelParser.parseContent(content); parseErrors = [...userConfigModelParser.errors]; return userConfigModelParser; diff --git a/src/vs/platform/configuration/test/common/testConfigurationService.ts b/src/vs/platform/configuration/test/common/testConfigurationService.ts index 9bb4c8e2208..e5551acf347 100644 --- a/src/vs/platform/configuration/test/common/testConfigurationService.ts +++ b/src/vs/platform/configuration/test/common/testConfigurationService.ts @@ -14,6 +14,8 @@ export class TestConfigurationService implements IConfigurationService { private configurationByRoot: TernarySearchTree = TernarySearchTree.forPaths(); + userSettingsResource = URI.file('settings.json'); + public reloadConfiguration(): Promise { return Promise.resolve(this.getValue()); } diff --git a/src/vs/platform/configuration/test/node/configurationService.test.ts b/src/vs/platform/configuration/test/node/configurationService.test.ts index 22fdbb6c911..3619e33bdb1 100644 --- a/src/vs/platform/configuration/test/node/configurationService.test.ts +++ b/src/vs/platform/configuration/test/node/configurationService.test.ts @@ -14,14 +14,15 @@ import * as uuid from 'vs/base/common/uuid'; import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry'; import { testFile } from 'vs/base/test/node/utils'; import { URI } from 'vs/base/common/uri'; +import { dirname } from 'vs/base/common/resources'; suite('ConfigurationService - Node', () => { test('simple', async () => { - const res = await testFile('config', 'config.json'); + const res = await testFile('config', 'settings.json'); fs.writeFileSync(res.testFile, '{ "foo": "bar" }'); - const service = new ConfigurationService(URI.file(res.testFile)); + const service = new ConfigurationService(dirname(URI.file(res.testFile))); await service.initialize(); const config = service.getValue<{ foo: string; @@ -35,11 +36,11 @@ suite('ConfigurationService - Node', () => { }); test('config gets flattened', async () => { - const res = await testFile('config', 'config.json'); + const res = await testFile('config', 'settings.json'); fs.writeFileSync(res.testFile, '{ "testworkbench.editor.tabs": true }'); - const service = new ConfigurationService(URI.file(res.testFile)); + const service = new ConfigurationService(dirname(URI.file(res.testFile))); await service.initialize(); const config = service.getValue<{ testworkbench: { @@ -58,11 +59,11 @@ suite('ConfigurationService - Node', () => { }); test('error case does not explode', async () => { - const res = await testFile('config', 'config.json'); + const res = await testFile('config', 'settings.json'); fs.writeFileSync(res.testFile, ',,,,'); - const service = new ConfigurationService(URI.file(res.testFile)); + const service = new ConfigurationService(dirname(URI.file(res.testFile))); await service.initialize(); const config = service.getValue<{ foo: string; @@ -77,9 +78,9 @@ suite('ConfigurationService - Node', () => { const id = uuid.generateUuid(); const parentDir = path.join(os.tmpdir(), 'vsctests', id); const newDir = path.join(parentDir, 'config', id); - const testFile = path.join(newDir, 'config.json'); + const testFile = path.join(newDir, 'settings.json'); - const service = new ConfigurationService(URI.file(testFile)); + const service = new ConfigurationService(dirname(URI.file(testFile))); await service.initialize(); const config = service.getValue<{ foo: string }>(); @@ -89,9 +90,9 @@ suite('ConfigurationService - Node', () => { }); test('trigger configuration change event', async () => { - const res = await testFile('config', 'config.json'); + const res = await testFile('config', 'settings.json'); - const service = new ConfigurationService(URI.file(res.testFile)); + const service = new ConfigurationService(dirname(URI.file(res.testFile))); await service.initialize(); return new Promise((c, e) => { service.onDidChangeConfiguration(() => { @@ -105,11 +106,11 @@ suite('ConfigurationService - Node', () => { }); test('reloadConfiguration', async () => { - const res = await testFile('config', 'config.json'); + const res = await testFile('config', 'settings.json'); fs.writeFileSync(res.testFile, '{ "foo": "bar" }'); - const service = new ConfigurationService(URI.file(res.testFile)); + const service = new ConfigurationService(dirname(URI.file(res.testFile))); await service.initialize(); let config = service.getValue<{ foo: string; @@ -158,17 +159,17 @@ suite('ConfigurationService - Node', () => { } }); - let serviceWithoutFile = new ConfigurationService(URI.file('__testFile')); + let serviceWithoutFile = new ConfigurationService(dirname(URI.file('__testFile'))); await serviceWithoutFile.initialize(); let setting = serviceWithoutFile.getValue(); assert.ok(setting); assert.equal(setting.configuration.service.testSetting, 'isSet'); - return testFile('config', 'config.json').then(async res => { + return testFile('config', 'settings.json').then(async res => { fs.writeFileSync(res.testFile, '{ "testworkbench.editor.tabs": true }'); - const service = new ConfigurationService(URI.file(res.testFile)); + const service = new ConfigurationService(dirname(URI.file(res.testFile))); let setting = service.getValue(); @@ -200,8 +201,8 @@ suite('ConfigurationService - Node', () => { } }); - const r = await testFile('config', 'config.json'); - const service = new ConfigurationService(URI.file(r.testFile)); + const r = await testFile('config', 'settings.json'); + const service = new ConfigurationService(dirname(URI.file(r.testFile))); service.initialize(); let res = service.inspect('something.missing'); @@ -238,8 +239,8 @@ suite('ConfigurationService - Node', () => { } }); - const r = await testFile('config', 'config.json'); - const service = new ConfigurationService(URI.file(r.testFile)); + const r = await testFile('config', 'settings.json'); + const service = new ConfigurationService(dirname(URI.file(r.testFile))); service.initialize(); let res = service.inspect('lookup.service.testNullSetting'); diff --git a/src/vs/platform/environment/common/environment.ts b/src/vs/platform/environment/common/environment.ts index 210ac5080a3..2679dd2d68b 100644 --- a/src/vs/platform/environment/common/environment.ts +++ b/src/vs/platform/environment/common/environment.ts @@ -97,7 +97,6 @@ export interface IEnvironmentService { appNameLong: string; appQuality?: string; appSettingsHome: URI; - settingsResource: URI; keybindingsResource: URI; keyboardLayoutResource: URI; diff --git a/src/vs/platform/environment/node/environmentService.ts b/src/vs/platform/environment/node/environmentService.ts index 12881effa53..e2b0934a468 100644 --- a/src/vs/platform/environment/node/environmentService.ts +++ b/src/vs/platform/environment/node/environmentService.ts @@ -114,9 +114,6 @@ export class EnvironmentService implements IEnvironmentService { @memoize get appSettingsHome(): URI { return URI.file(path.join(this.userDataPath, 'User')); } - @memoize - get settingsResource(): URI { return resources.joinPath(this.appSettingsHome, 'settings.json'); } - @memoize get machineSettingsHome(): URI { return URI.file(path.join(this.userDataPath, 'Machine')); } diff --git a/src/vs/platform/telemetry/test/electron-browser/telemetryService.test.ts b/src/vs/platform/telemetry/test/electron-browser/telemetryService.test.ts index 9f728745344..530ffae4699 100644 --- a/src/vs/platform/telemetry/test/electron-browser/telemetryService.test.ts +++ b/src/vs/platform/telemetry/test/electron-browser/telemetryService.test.ts @@ -11,6 +11,7 @@ import * as Errors from 'vs/base/common/errors'; import * as sinon from 'sinon'; import { getConfigurationValue } from 'vs/platform/configuration/common/configuration'; import { ITelemetryData } from 'vs/platform/telemetry/common/telemetry'; +import { URI } from 'vs/base/common/uri'; class TestTelemetryAppender implements ITelemetryAppender { @@ -769,6 +770,7 @@ suite('TelemetryService', () => { appender: testAppender }, { _serviceBrand: undefined, + userSettingsResource: URI.file('settings.json'), getValue() { return { enableTelemetry: enableTelemetry diff --git a/src/vs/platform/windows/electron-main/windowsService.ts b/src/vs/platform/windows/electron-main/windowsService.ts index 1c3c4734384..80ef92456ae 100644 --- a/src/vs/platform/windows/electron-main/windowsService.ts +++ b/src/vs/platform/windows/electron-main/windowsService.ts @@ -342,6 +342,9 @@ export class WindowsService extends Disposable implements IWindowsService, IURLH if (path.scheme === Schemas.file) { shell.showItemInFolder(path.fsPath); } + if (path.scheme === Schemas.userData) { + shell.showItemInFolder(path.path); + } } async getActiveWindowId(): Promise { diff --git a/src/vs/workbench/browser/web.main.ts b/src/vs/workbench/browser/web.main.ts index 9b4051ac410..3e64ed90871 100644 --- a/src/vs/workbench/browser/web.main.ts +++ b/src/vs/workbench/browser/web.main.ts @@ -35,8 +35,9 @@ import { SignService } from 'vs/platform/sign/browser/signService'; import { hash } from 'vs/base/common/hash'; import { IWorkbenchConstructionOptions } from 'vs/workbench/workbench.web.api'; import { ProductService } from 'vs/platform/product/browser/productService'; -import { FileUserDataService } from '../services/userData/common/fileUserDataService'; -import { IUserDataService } from '../services/userData/common/userDataService'; +import { FileUserDataService } from 'vs/workbench/services/userData/common/fileUserDataService'; +import { IUserDataService } from 'vs/workbench//services/userData/common/userDataService'; +import { UserDataFileProvider } from 'vs/workbench//services/userData/common/userDataFileProvider'; class CodeRendererMain extends Disposable { @@ -122,6 +123,7 @@ class CodeRendererMain extends Disposable { // User Data Service const userDataService = this._register(new FileUserDataService(environmentService, fileService)); serviceCollection.set(IUserDataService, userDataService); + fileService.registerProvider(Schemas.userData, new UserDataFileProvider(userDataService)); const payload = await this.resolveWorkspaceInitializationPayload(); diff --git a/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts b/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts index 8573f02c5a2..152668b7c7f 100644 --- a/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts +++ b/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts @@ -167,7 +167,6 @@ const copyRelativePathCommand = { // Editor Title Context Menu appendEditorTitleContextMenuItem(COPY_PATH_COMMAND_ID, copyPathCommand.title, ResourceContextKey.IsFileSystemResource, '1_cutcopypaste'); appendEditorTitleContextMenuItem(COPY_RELATIVE_PATH_COMMAND_ID, copyRelativePathCommand.title, ResourceContextKey.IsFileSystemResource, '1_cutcopypaste'); -appendEditorTitleContextMenuItem(REVEAL_IN_OS_COMMAND_ID, REVEAL_IN_OS_LABEL, ResourceContextKey.Scheme.isEqualTo(Schemas.file)); appendEditorTitleContextMenuItem(REVEAL_IN_EXPLORER_COMMAND_ID, nls.localize('revealInSideBar', "Reveal in Side Bar"), ResourceContextKey.IsFileSystemResource); function appendEditorTitleContextMenuItem(id: string, title: string, when: ContextKeyExpr, group?: string): void { diff --git a/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts b/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts index 20c9985f632..cf6d46bc632 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts @@ -39,6 +39,7 @@ import { ExplorerRootContext, ExplorerFolderContext } from 'vs/workbench/contrib import { ILabelService } from 'vs/platform/label/common/label'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; Registry.as(EditorExtensions.Editors).registerEditor( new EditorDescriptor( @@ -370,6 +371,7 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon constructor( @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService, + @IConfigurationService configurationService: IConfigurationService, @IPreferencesService private readonly preferencesService: IPreferencesService, @IWorkspaceContextService private readonly workpsaceContextService: IWorkspaceContextService, @ILabelService labelService: ILabelService, @@ -401,7 +403,7 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon dark: URI.parse(require.toUrl(`vs/workbench/contrib/preferences/browser/media/preferences-editor-inverse.svg`)) } }, - when: ResourceContextKey.Resource.isEqualTo(environmentService.settingsResource.toString()), + when: ResourceContextKey.Resource.isEqualTo(configurationService.userSettingsResource.toString()), group: 'navigation', order: 1 }); diff --git a/src/vs/workbench/contrib/preferences/common/preferencesContribution.ts b/src/vs/workbench/contrib/preferences/common/preferencesContribution.ts index 246eaf2ed11..52097f9afc2 100644 --- a/src/vs/workbench/contrib/preferences/common/preferencesContribution.ts +++ b/src/vs/workbench/contrib/preferences/common/preferencesContribution.ts @@ -14,7 +14,6 @@ import { IModeService } from 'vs/editor/common/services/modeService'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IEditorOptions, ITextEditorOptions } from 'vs/platform/editor/common/editor'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import * as JSONContributionRegistry from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; import { Registry } from 'vs/platform/registry/common/platform'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; @@ -36,7 +35,6 @@ export class PreferencesContribution implements IWorkbenchContribution { @IPreferencesService private readonly preferencesService: IPreferencesService, @IModeService private readonly modeService: IModeService, @IEditorService private readonly editorService: IEditorService, - @IEnvironmentService private readonly environmentService: IEnvironmentService, @IWorkspaceContextService private readonly workspaceService: IWorkspaceContextService, @IConfigurationService private readonly configurationService: IConfigurationService ) { @@ -79,7 +77,7 @@ export class PreferencesContribution implements IWorkbenchContribution { } // Global User Settings File - if (isEqual(resource, this.environmentService.settingsResource, !isLinux)) { + if (isEqual(resource, this.configurationService.userSettingsResource, !isLinux)) { return { override: this.preferencesService.openGlobalSettings(true, options, group) }; } diff --git a/src/vs/workbench/electron-browser/main.contribution.ts b/src/vs/workbench/electron-browser/main.contribution.ts index aa6647f914f..b31b01cdfc5 100644 --- a/src/vs/workbench/electron-browser/main.contribution.ts +++ b/src/vs/workbench/electron-browser/main.contribution.ts @@ -26,6 +26,9 @@ import { NoEditorsVisibleContext, SingleEditorGroupsContext } from 'vs/workbench import { IWindowService, IWindowsService } from 'vs/platform/windows/common/windows'; import { LogStorageAction } from 'vs/platform/storage/node/storageService'; import product from 'vs/platform/product/node/product'; +import { REVEAL_IN_OS_COMMAND_ID, REVEAL_IN_OS_LABEL } from '../contrib/files/browser/fileCommands'; +import { ResourceContextKey } from 'vs/workbench/common/resources'; +import { Schemas } from 'vs/base/common/network'; // Actions (function registerActions(): void { @@ -61,6 +64,17 @@ import product from 'vs/platform/product/node/product'; primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_R, mac: { primary: KeyMod.WinCtrl | KeyMod.Shift | KeyCode.KEY_R } }); + + MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { + command: { id: REVEAL_IN_OS_COMMAND_ID, title: REVEAL_IN_OS_LABEL }, + when: ResourceContextKey.Scheme.isEqualTo(Schemas.file), + group: '2_files' + }); + MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { + command: { id: REVEAL_IN_OS_COMMAND_ID, title: REVEAL_IN_OS_LABEL }, + when: ResourceContextKey.Scheme.isEqualTo(Schemas.userData), + group: '2_files' + }); })(); // Actions: View diff --git a/src/vs/workbench/electron-browser/main.ts b/src/vs/workbench/electron-browser/main.ts index 2d11615deb9..1dc0f324175 100644 --- a/src/vs/workbench/electron-browser/main.ts +++ b/src/vs/workbench/electron-browser/main.ts @@ -52,6 +52,7 @@ import { SignService } from 'vs/platform/sign/node/signService'; import { ISignService } from 'vs/platform/sign/common/sign'; import { IUserDataService } from '../services/userData/common/userDataService'; import { FileUserDataService } from '../services/userData/common/fileUserDataService'; +import { UserDataFileProvider } from '../services/userData/common/userDataFileProvider'; class CodeRendererMain extends Disposable { @@ -210,6 +211,7 @@ class CodeRendererMain extends Disposable { // User Data Service const userDataService = this._register(new FileUserDataService(environmentService, fileService)); serviceCollection.set(IUserDataService, userDataService); + fileService.registerProvider(Schemas.userData, new UserDataFileProvider(userDataService)); const payload = await this.resolveWorkspaceInitializationPayload(environmentService); diff --git a/src/vs/workbench/services/configuration/browser/configuration.ts b/src/vs/workbench/services/configuration/browser/configuration.ts index 14bade52756..38b605b5d89 100644 --- a/src/vs/workbench/services/configuration/browser/configuration.ts +++ b/src/vs/workbench/services/configuration/browser/configuration.ts @@ -23,10 +23,12 @@ import { Schemas } from 'vs/base/common/network'; import { IConfigurationModel } from 'vs/platform/configuration/common/configuration'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { hash } from 'vs/base/common/hash'; -import { IUserDataService } from '../../userData/common/userDataService'; +import { IUserDataService } from 'vs/workbench/services/userData/common/userDataService'; export class UserConfiguration extends Disposable { + readonly resource: URI; + private readonly parser: ConfigurationModelParser; private readonly reloadConfigurationScheduler: RunOnceScheduler; protected readonly _onDidChangeConfiguration: Emitter = this._register(new Emitter()); @@ -38,6 +40,7 @@ export class UserConfiguration extends Disposable { ) { super(); + this.resource = userDataService.toResource(USER_CONFIGURATION_KEY); this.parser = new ConfigurationModelParser(USER_CONFIGURATION_KEY, this.scopes); this.reloadConfigurationScheduler = this._register(new RunOnceScheduler(() => this.reload().then(configurationModel => this._onDidChangeConfiguration.fire(configurationModel)), 50)); this._register(Event.filter(this.userDataService.onDidChange, e => e.contains(USER_CONFIGURATION_KEY))(() => this.reloadConfigurationScheduler.schedule())); diff --git a/src/vs/workbench/services/configuration/browser/configurationService.ts b/src/vs/workbench/services/configuration/browser/configurationService.ts index bf934755350..4c5eb4e2e57 100644 --- a/src/vs/workbench/services/configuration/browser/configurationService.ts +++ b/src/vs/workbench/services/configuration/browser/configurationService.ts @@ -47,6 +47,7 @@ export class WorkspaceService extends Disposable implements IConfigurationServic private cachedFolderConfigs: ResourceMap; private workspaceEditingQueue: Queue; + readonly userSettingsResource: URI; private readonly configurationFileService: ConfigurationFileService; protected readonly _onDidChangeConfiguration: Emitter = this._register(new Emitter()); @@ -82,6 +83,7 @@ export class WorkspaceService extends Disposable implements IConfigurationServic this._configuration = new Configuration(this.defaultConfiguration, new ConfigurationModel(), new ConfigurationModel(), new ConfigurationModel(), new ResourceMap(), new ConfigurationModel(), new ResourceMap(), this.workspace); this.cachedFolderConfigs = new ResourceMap(); this.localUserConfiguration = this._register(new UserConfiguration(remoteAuthority ? LOCAL_MACHINE_SCOPES : undefined, userDataService)); + this.userSettingsResource = this.localUserConfiguration.resource; this._register(this.localUserConfiguration.onDidChangeConfiguration(userConfiguration => this.onLocalUserConfigurationChanged(userConfiguration))); if (remoteAuthority) { this.remoteUserConfiguration = this._register(new RemoteUserConfiguration(remoteAuthority, configurationCache, this.configurationFileService, remoteAgentService)); diff --git a/src/vs/workbench/services/configuration/common/configurationEditingService.ts b/src/vs/workbench/services/configuration/common/configurationEditingService.ts index 827452f20a7..dd6e767be62 100644 --- a/src/vs/workbench/services/configuration/common/configurationEditingService.ts +++ b/src/vs/workbench/services/configuration/common/configurationEditingService.ts @@ -34,6 +34,7 @@ import { Emitter } from 'vs/base/common/event'; import { LanguageIdentifier } from 'vs/editor/common/modes'; import { IDisposable, Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { Schemas } from 'vs/base/common/network'; export const enum ConfigurationEditingErrorCode { @@ -439,7 +440,7 @@ class UserConfigurationEditOperation extends ConfigurationEditOperation { private async _resolve(): Promise { const content = (await this.userDataService.read(USER_CONFIGURATION_KEY)) || '{}'; const languageIdentifier = this.modeService.getLanguageIdentifier('jsonc'); - const model = this.modelService.createModel(content, languageIdentifier ? { languageIdentifier, onDidChange: new Emitter().event, dispose: () => { } } : null); + const model = this.modelService.createModel(content, languageIdentifier ? { languageIdentifier, onDidChange: new Emitter().event, dispose: () => { } } : null, this.configurationService.userSettingsResource.with({ scheme: Schemas.vscode })); this._register(toDisposable(() => { model.dispose(); this.modelService.destroyModel(model.uri); diff --git a/src/vs/workbench/services/configuration/test/electron-browser/configurationEditingService.test.ts b/src/vs/workbench/services/configuration/test/electron-browser/configurationEditingService.test.ts index 8f2b128f1dd..063a8f58d04 100644 --- a/src/vs/workbench/services/configuration/test/electron-browser/configurationEditingService.test.ts +++ b/src/vs/workbench/services/configuration/test/electron-browser/configurationEditingService.test.ts @@ -49,8 +49,7 @@ class SettingsTestEnvironmentService extends EnvironmentService { super(args, _execPath); } - get appSettingsHome(): URI { return dirname(this.settingsResource); } - get settingsResource(): URI { return URI.file(this._settingsPath); } + get appSettingsHome(): URI { return dirname(URI.file(this._settingsPath)); } } suite('ConfigurationEditingService', () => { diff --git a/src/vs/workbench/services/configuration/test/electron-browser/configurationService.test.ts b/src/vs/workbench/services/configuration/test/electron-browser/configurationService.test.ts index 3e1e4be3e3c..663a11973e3 100644 --- a/src/vs/workbench/services/configuration/test/electron-browser/configurationService.test.ts +++ b/src/vs/workbench/services/configuration/test/electron-browser/configurationService.test.ts @@ -46,6 +46,7 @@ import { IConfigurationCache } from 'vs/workbench/services/configuration/common/ import { VSBuffer } from 'vs/base/common/buffer'; import { SignService } from 'vs/platform/sign/browser/signService'; import { FileUserDataService } from 'vs/workbench/services/userData/common/fileUserDataService'; +import { IUserDataService } from 'vs/workbench/services/userData/common/userDataService'; class SettingsTestEnvironmentService extends EnvironmentService { @@ -53,8 +54,7 @@ class SettingsTestEnvironmentService extends EnvironmentService { super(args, _execPath); } - get appSettingsHome(): URI { return dirname(this.settingsResource); } - get settingsResource(): URI { return URI.file(this._settingsPath); } + get appSettingsHome(): URI { return dirname(URI.file(this._settingsPath)); } } function setUpFolderWorkspace(folderName: string): Promise<{ parentDir: string, folderDir: string }> { @@ -754,6 +754,7 @@ suite('WorkspaceConfigurationService - Folder', () => { const fileService = new FileService(new NullLogService()); fileService.registerProvider(Schemas.file, new DiskFileSystemProvider(new NullLogService())); const userDataService = new FileUserDataService(environmentService, fileService); + instantiationService.stub(IUserDataService, userDataService); const workspaceService = new WorkspaceService({ configurationCache: new ConfigurationCache(environmentService) }, fileService, userDataService, remoteAgentService); instantiationService.stub(IWorkspaceContextService, workspaceService); instantiationService.stub(IConfigurationService, workspaceService); @@ -1037,7 +1038,7 @@ suite('WorkspaceConfigurationService - Folder', () => { suite('WorkspaceConfigurationService-Multiroot', () => { - let parentResource: string, workspaceContextService: IWorkspaceContextService, environmentService: IEnvironmentService, jsonEditingServce: IJSONEditingService, testObject: IConfigurationService; + let parentResource: string, workspaceContextService: IWorkspaceContextService, environmentService: IEnvironmentService, jsonEditingServce: IJSONEditingService, testObject: IConfigurationService, globalSettingsFile: string; const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); suiteSetup(() => { @@ -1073,14 +1074,16 @@ suite('WorkspaceConfigurationService-Multiroot', () => { .then(({ parentDir, configPath }) => { parentResource = parentDir; + globalSettingsFile = path.join(parentDir, 'settings.json'); const instantiationService = workbenchInstantiationService(); - environmentService = new SettingsTestEnvironmentService(parseArgs(process.argv), process.execPath, path.join(parentDir, 'settings.json')); + environmentService = new SettingsTestEnvironmentService(parseArgs(process.argv), process.execPath, globalSettingsFile); const remoteAgentService = instantiationService.createInstance(RemoteAgentService, {}); instantiationService.stub(IRemoteAgentService, remoteAgentService); const fileService = new FileService(new NullLogService()); fileService.registerProvider(Schemas.file, new DiskFileSystemProvider(new NullLogService())); const userDataService = new FileUserDataService(environmentService, fileService); + instantiationService.stub(IUserDataService, userDataService); const workspaceService = new WorkspaceService({ configurationCache: new ConfigurationCache(environmentService) }, fileService, userDataService, remoteAgentService); instantiationService.stub(IWorkspaceContextService, workspaceService); @@ -1111,21 +1114,21 @@ suite('WorkspaceConfigurationService-Multiroot', () => { }); test('application settings are not read from workspace', () => { - fs.writeFileSync(environmentService.settingsResource.fsPath, '{ "configurationService.workspace.applicationSetting": "userValue" }'); + fs.writeFileSync(globalSettingsFile, '{ "configurationService.workspace.applicationSetting": "userValue" }'); return jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, { key: 'settings', value: { 'configurationService.workspace.applicationSetting': 'workspaceValue' } }, true) .then(() => testObject.reloadConfiguration()) .then(() => assert.equal(testObject.getValue('configurationService.workspace.applicationSetting'), 'userValue')); }); test('machine settings are not read from workspace', () => { - fs.writeFileSync(environmentService.settingsResource.fsPath, '{ "configurationService.workspace.machineSetting": "userValue" }'); + fs.writeFileSync(globalSettingsFile, '{ "configurationService.workspace.machineSetting": "userValue" }'); return jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, { key: 'settings', value: { 'configurationService.workspace.machineSetting': 'workspaceValue' } }, true) .then(() => testObject.reloadConfiguration()) .then(() => assert.equal(testObject.getValue('configurationService.workspace.machineSetting'), 'userValue')); }); test('workspace settings override user settings after defaults are registered ', () => { - fs.writeFileSync(environmentService.settingsResource.fsPath, '{ "configurationService.workspace.newSetting": "userValue" }'); + fs.writeFileSync(globalSettingsFile, '{ "configurationService.workspace.newSetting": "userValue" }'); return jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, { key: 'settings', value: { 'configurationService.workspace.newSetting': 'workspaceValue' } }, true) .then(() => testObject.reloadConfiguration()) .then(() => { @@ -1144,21 +1147,21 @@ suite('WorkspaceConfigurationService-Multiroot', () => { }); test('application settings are not read from workspace folder', () => { - fs.writeFileSync(environmentService.settingsResource.fsPath, '{ "configurationService.workspace.applicationSetting": "userValue" }'); + fs.writeFileSync(globalSettingsFile, '{ "configurationService.workspace.applicationSetting": "userValue" }'); fs.writeFileSync(workspaceContextService.getWorkspace().folders[0].toResource('.vscode/settings.json').fsPath, '{ "configurationService.workspace.applicationSetting": "workspaceFolderValue" }'); return testObject.reloadConfiguration() .then(() => assert.equal(testObject.getValue('configurationService.workspace.applicationSetting'), 'userValue')); }); test('machine settings are not read from workspace folder', () => { - fs.writeFileSync(environmentService.settingsResource.fsPath, '{ "configurationService.workspace.machineSetting": "userValue" }'); + fs.writeFileSync(globalSettingsFile, '{ "configurationService.workspace.machineSetting": "userValue" }'); fs.writeFileSync(workspaceContextService.getWorkspace().folders[0].toResource('.vscode/settings.json').fsPath, '{ "configurationService.workspace.machineSetting": "workspaceFolderValue" }'); return testObject.reloadConfiguration() .then(() => assert.equal(testObject.getValue('configurationService.workspace.machineSetting'), 'userValue')); }); test('application settings are not read from workspace folder after defaults are registered', () => { - fs.writeFileSync(environmentService.settingsResource.fsPath, '{ "configurationService.workspace.testNewApplicationSetting": "userValue" }'); + fs.writeFileSync(globalSettingsFile, '{ "configurationService.workspace.testNewApplicationSetting": "userValue" }'); fs.writeFileSync(workspaceContextService.getWorkspace().folders[0].toResource('.vscode/settings.json').fsPath, '{ "configurationService.workspace.testNewApplicationSetting": "workspaceFolderValue" }'); return testObject.reloadConfiguration() .then(() => { @@ -1178,7 +1181,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { }); test('application settings are not read from workspace folder after defaults are registered', () => { - fs.writeFileSync(environmentService.settingsResource.fsPath, '{ "configurationService.workspace.testNewMachineSetting": "userValue" }'); + fs.writeFileSync(globalSettingsFile, '{ "configurationService.workspace.testNewMachineSetting": "userValue" }'); fs.writeFileSync(workspaceContextService.getWorkspace().folders[0].toResource('.vscode/settings.json').fsPath, '{ "configurationService.workspace.testNewMachineSetting": "workspaceFolderValue" }'); return testObject.reloadConfiguration() .then(() => { @@ -1232,7 +1235,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { assert.equal(actual.workspaceFolder, undefined); assert.equal(actual.value, 'isSet'); - fs.writeFileSync(environmentService.settingsResource.fsPath, '{ "configurationService.workspace.testResourceSetting": "userValue" }'); + fs.writeFileSync(globalSettingsFile, '{ "configurationService.workspace.testResourceSetting": "userValue" }'); return testObject.reloadConfiguration() .then(() => { actual = testObject.inspect('configurationService.workspace.testResourceSetting'); diff --git a/src/vs/workbench/services/configurationResolver/test/electron-browser/configurationResolverService.test.ts b/src/vs/workbench/services/configurationResolver/test/electron-browser/configurationResolverService.test.ts index cefee9701c9..d875310f3af 100644 --- a/src/vs/workbench/services/configurationResolver/test/electron-browser/configurationResolverService.test.ts +++ b/src/vs/workbench/services/configurationResolver/test/electron-browser/configurationResolverService.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { URI as uri } from 'vs/base/common/uri'; +import { URI as uri, URI } from 'vs/base/common/uri'; import * as platform from 'vs/base/common/platform'; import { IConfigurationService, getConfigurationValue, IConfigurationOverrides } from 'vs/platform/configuration/common/configuration'; import { ICommandService } from 'vs/platform/commands/common/commands'; @@ -494,6 +494,7 @@ suite('Configuration Resolver Service', () => { class MockConfigurationService implements IConfigurationService { public _serviceBrand: any; public serviceId = IConfigurationService; + userSettingsResource = URI.file('settings.json'); public constructor(private configuration: any = {}) { } public inspect(key: string, overrides?: IConfigurationOverrides): any { return { value: getConfigurationValue(this.getValue(), key), default: getConfigurationValue(this.getValue(), key), user: getConfigurationValue(this.getValue(), key), workspaceFolder: undefined, folder: undefined }; } public keys() { return { default: [], user: [], workspace: [], workspaceFolder: [] }; } diff --git a/src/vs/workbench/services/environment/browser/environmentService.ts b/src/vs/workbench/services/environment/browser/environmentService.ts index 3525569601e..e3911e2833a 100644 --- a/src/vs/workbench/services/environment/browser/environmentService.ts +++ b/src/vs/workbench/services/environment/browser/environmentService.ts @@ -71,7 +71,6 @@ export class BrowserWorkbenchEnvironmentService implements IEnvironmentService { this.configuration.remoteAuthority = configuration.remoteAuthority; this.appSettingsHome = joinPath(URI.revive(JSON.parse(document.getElementById('vscode-remote-user-data-uri')!.getAttribute('data-settings')!)), 'User'); - this.settingsResource = joinPath(this.appSettingsHome, 'settings.json'); this.keybindingsResource = joinPath(this.appSettingsHome, 'keybindings.json'); this.keyboardLayoutResource = joinPath(this.appSettingsHome, 'keyboardLayout.json'); @@ -97,7 +96,6 @@ export class BrowserWorkbenchEnvironmentService implements IEnvironmentService { appNameLong: string; appQuality?: string; appSettingsHome: URI; - settingsResource: URI; keybindingsResource: URI; keyboardLayoutResource: URI; machineSettingsHome: URI; diff --git a/src/vs/workbench/services/keybinding/test/electron-browser/keybindingEditing.test.ts b/src/vs/workbench/services/keybinding/test/electron-browser/keybindingEditing.test.ts index 255ec9727fc..f91983980e5 100644 --- a/src/vs/workbench/services/keybinding/test/electron-browser/keybindingEditing.test.ts +++ b/src/vs/workbench/services/keybinding/test/electron-browser/keybindingEditing.test.ts @@ -66,7 +66,7 @@ suite('KeybindingsEditing', () => { instantiationService = new TestInstantiationService(); - instantiationService.stub(IEnvironmentService, { keybindingsResource: URI.file(keybindingsFile), settingsResource: URI.file(path.join(testDir, 'settings.json')) }); + instantiationService.stub(IEnvironmentService, { keybindingsResource: URI.file(keybindingsFile) }); instantiationService.stub(IConfigurationService, ConfigurationService); instantiationService.stub(IConfigurationService, 'getValue', { 'eol': '\n' }); instantiationService.stub(IConfigurationService, 'onDidUpdateConfiguration', () => { }); diff --git a/src/vs/workbench/services/preferences/browser/preferencesService.ts b/src/vs/workbench/services/preferences/browser/preferencesService.ts index 9f6330fcd37..2de8150a4c4 100644 --- a/src/vs/workbench/services/preferences/browser/preferencesService.ts +++ b/src/vs/workbench/services/preferences/browser/preferencesService.ts @@ -524,9 +524,9 @@ export class PreferencesService extends Disposable implements IPreferencesServic switch (configurationTarget) { case ConfigurationTarget.USER: case ConfigurationTarget.USER_LOCAL: - return this.environmentService.settingsResource; + return this.configurationService.userSettingsResource; case ConfigurationTarget.USER_REMOTE: - return this.environmentService.settingsResource; + return this.configurationService.userSettingsResource; case ConfigurationTarget.WORKSPACE: if (this.contextService.getWorkbenchState() === WorkbenchState.EMPTY) { return null; diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts index 9a70fd94b6a..866ee212c2e 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts @@ -30,6 +30,7 @@ import { ILogService } from 'vs/platform/log/common/log'; import { isEqual, isEqualOrParent, extname, basename, joinPath } from 'vs/base/common/resources'; import { onUnexpectedError } from 'vs/base/common/errors'; import { Schemas } from 'vs/base/common/network'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; export interface IBackupMetaData { mtime: number; @@ -104,7 +105,8 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil @IBackupFileService private readonly backupFileService: IBackupFileService, @IEnvironmentService private readonly environmentService: IEnvironmentService, @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, - @ILogService private readonly logService: ILogService + @ILogService private readonly logService: ILogService, + @IConfigurationService private readonly configurationService: IConfigurationService ) { super(modelService, modeService); @@ -775,7 +777,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil } // Check for global settings file - if (isEqual(this.resource, this.environmentService.settingsResource, !isLinux)) { + if (isEqual(this.resource, this.configurationService.userSettingsResource, !isLinux)) { return 'global-settings'; } diff --git a/src/vs/workbench/services/textfile/common/textResourcePropertiesService.ts b/src/vs/workbench/services/textfile/common/textResourcePropertiesService.ts index ce097d6fdf5..14a488aafc6 100644 --- a/src/vs/workbench/services/textfile/common/textResourcePropertiesService.ts +++ b/src/vs/workbench/services/textfile/common/textResourcePropertiesService.ts @@ -44,7 +44,7 @@ export class TextResourcePropertiesService implements ITextResourcePropertiesSer const remoteAuthority = this.environmentService.configuration.remoteAuthority; if (remoteAuthority) { - if (resource.scheme !== Schemas.file) { + if (resource.scheme === Schemas.vscodeRemote) { const osCacheKey = `resource.authority.os.${remoteAuthority}`; os = this.remoteEnvironment ? this.remoteEnvironment.os : /* Get it from cache */ this.storageService.getNumber(osCacheKey, StorageScope.WORKSPACE, OS); this.storageService.store(osCacheKey, os, StorageScope.WORKSPACE); diff --git a/src/vs/workbench/services/userData/common/fileUserDataService.ts b/src/vs/workbench/services/userData/common/fileUserDataService.ts index 6096274dc6d..ae4ac73cb3b 100644 --- a/src/vs/workbench/services/userData/common/fileUserDataService.ts +++ b/src/vs/workbench/services/userData/common/fileUserDataService.ts @@ -12,6 +12,7 @@ import * as resources from 'vs/base/common/resources'; import { TernarySearchTree } from 'vs/base/common/map'; import { VSBuffer } from 'vs/base/common/buffer'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { Schemas } from 'vs/base/common/network'; export class FileUserDataService extends Disposable implements IUserDataService { _serviceBrand: any; @@ -36,9 +37,11 @@ export class FileUserDataService extends Disposable implements IUserDataService private handleFileChanges(event: FileChangesEvent): void { const changedKeys: string[] = []; for (const change of event.changes) { - const key = resources.relativePath(this.settingsHome, change.resource); - if (key) { - changedKeys.push(key); + if (change.resource.scheme !== Schemas.userData) { + const key = this.toKey(change.resource.with({ scheme: Schemas.userData })); + if (key) { + changedKeys.push(key); + } } } if (changedKeys.length) { @@ -47,7 +50,7 @@ export class FileUserDataService extends Disposable implements IUserDataService } async read(key: string): Promise { - const resource = this.toResource(key); + const resource = this.toFileResource(key); try { const content = await this.fileService.readFile(resource); return content.value.toString(); @@ -61,13 +64,21 @@ export class FileUserDataService extends Disposable implements IUserDataService } write(key: string, value: string): Promise { - return this.fileService.writeFile(this.toResource(key), VSBuffer.fromString(value)).then(() => undefined); + return this.fileService.writeFile(this.toFileResource(key), VSBuffer.fromString(value)).then(() => undefined); } - private toResource(key: string): URI { + private toFileResource(key: string): URI { return resources.joinPath(this.settingsHome, ...key.split('/')); } + toResource(key: string): URI { + return this.toFileResource(key).with({ scheme: Schemas.userData }); + } + + toKey(resource: URI): string | undefined { + return resources.relativePath(this.settingsHome.with({ scheme: Schemas.userData }), resource); + } + } class UserDataChangesEvent implements IUserDataChangesEvent { diff --git a/src/vs/workbench/services/userData/common/userDataFileProvider.ts b/src/vs/workbench/services/userData/common/userDataFileProvider.ts new file mode 100644 index 00000000000..76e4419a51d --- /dev/null +++ b/src/vs/workbench/services/userData/common/userDataFileProvider.ts @@ -0,0 +1,72 @@ +/*--------------------------------------------------------------------------------------------- + * 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, FileChangesEvent, FileChangeType } from 'vs/platform/files/common/files'; +import { IUserDataService } from './userDataService'; +import { URI } from 'vs/base/common/uri'; +import { VSBuffer } from 'vs/base/common/buffer'; +import { Event, Emitter } from 'vs/base/common/event'; + +export class UserDataFileProvider extends Disposable implements IFileSystemProviderWithFileReadWriteCapability { + + constructor(private readonly userDataService: IUserDataService) { super(); } + + readonly capabilities: FileSystemProviderCapabilities = FileSystemProviderCapabilities.FileReadWrite; + readonly onDidChangeCapabilities: Event = Event.None; + + private readonly _onDidChangeFile: Emitter = this._register(new Emitter()); + readonly onDidChangeFile: Event = this._onDidChangeFile.event; + + private versions: Map = new Map(); + + watch(resource: URI, opts: IWatchOptions): IDisposable { + const key = this.userDataService.toKey(resource); + if (!key) { + throw new Error(`Invalud user data resource ${resource}`); + } + return this.userDataService.onDidChange(e => { + if (e.contains(key)) { + this.versions.set(key, (this.versions.get(key) || 1) + 1); + this._onDidChangeFile.fire(new FileChangesEvent([{ resource, type: FileChangeType.UPDATED }]).changes); + } + }); + } + + async stat(resource: URI): Promise { + const key = this.userDataService.toKey(resource); + if (!key) { + throw new Error(`Invalud user data resource ${resource}`); + } + return { + type: FileType.File, + ctime: 0, + mtime: this.versions.get(key) || 0, + size: 0 + }; + } + mkdir(resource: URI): Promise { throw new Error('not supported'); } + readdir(resource: URI): Promise<[string, FileType][]> { throw new Error('not supported'); } + delete(resource: URI, opts: FileDeleteOptions): Promise { throw new Error('not supported'); } + + rename(from: URI, to: URI, opts: FileOverwriteOptions): Promise { throw new Error('not supported'); } + + async readFile(resource: URI): Promise { + const key = this.userDataService.toKey(resource); + if (!key) { + throw new Error(`Invalud user data resource ${resource}`); + } + const content = await this.userDataService.read(key); + return VSBuffer.fromString(content).buffer; + } + + writeFile(resource: URI, content: Uint8Array, opts: FileWriteOptions): Promise { + const key = this.userDataService.toKey(resource); + if (!key) { + throw new Error(`Invalud user data resource ${resource}`); + } + return this.userDataService.write(key, VSBuffer.wrap(content).toString()); + } +} \ No newline at end of file diff --git a/src/vs/workbench/services/userData/common/userDataService.ts b/src/vs/workbench/services/userData/common/userDataService.ts index 81c8b5dfd76..d47f01616e2 100644 --- a/src/vs/workbench/services/userData/common/userDataService.ts +++ b/src/vs/workbench/services/userData/common/userDataService.ts @@ -5,7 +5,9 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { Event } from 'vs/base/common/event'; +import { URI } from 'vs/base/common/uri'; +export const schme: string = 'vscode-userdata'; export const IUserDataService = createDecorator('userDataService'); export interface IUserDataChangesEvent { @@ -18,6 +20,10 @@ export interface IUserDataService { onDidChange: Event; + toResource(key: string): URI; + + toKey(resource: URI): string | undefined; + read(key: string): Promise; write(key: string, value: string): Promise; -- GitLab