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

UserDataFileProvider for handling user data resources

上级 89640033
......@@ -46,4 +46,6 @@ export namespace Schemas {
export const command: string = 'command';
export const vscodeRemote: string = 'vscode-remote';
export const userData: string = 'vscode-userdata';
}
......@@ -16,8 +16,8 @@ export interface ITestFileResult {
export function testFile(folder: string, file: string): Promise<ITestFileResult> {
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 {
......
......@@ -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();
......
......@@ -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));
......
......@@ -285,7 +285,7 @@ export async function main(argv: ParsedArgs): Promise<void> {
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);
......
......@@ -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());
}
......
......@@ -65,6 +65,7 @@ export interface IConfigurationChangeEvent {
export interface IConfigurationService {
_serviceBrand: any;
userSettingsResource: URI;
onDidChangeConfiguration: Event<IConfigurationChangeEvent>;
getConfigurationData(): IConfigurationData | null;
......
......@@ -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<ConfigurationModelParser> | undefined;
......@@ -26,9 +29,10 @@ export class ConfigurationService extends Disposable implements IConfigurationSe
readonly onDidChangeConfiguration: Event<IConfigurationChangeEvent> = 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<IConfigurationRegistry>(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<void>((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;
......
......@@ -14,6 +14,8 @@ export class TestConfigurationService implements IConfigurationService {
private configurationByRoot: TernarySearchTree<any> = TernarySearchTree.forPaths<any>();
userSettingsResource = URI.file('settings.json');
public reloadConfiguration<T>(): Promise<T> {
return Promise.resolve(this.getValue());
}
......
......@@ -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<ITestSetting>();
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<ITestSetting>();
......@@ -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');
......
......@@ -97,7 +97,6 @@ export interface IEnvironmentService {
appNameLong: string;
appQuality?: string;
appSettingsHome: URI;
settingsResource: URI;
keybindingsResource: URI;
keyboardLayoutResource: URI;
......
......@@ -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')); }
......
......@@ -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
......
......@@ -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<number | undefined> {
......
......@@ -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();
......
......@@ -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 {
......
......@@ -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<IEditorRegistry>(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
});
......
......@@ -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) };
}
......
......@@ -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
......
......@@ -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);
......
......@@ -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<ConfigurationModel> = this._register(new Emitter<ConfigurationModel>());
......@@ -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()));
......
......@@ -47,6 +47,7 @@ export class WorkspaceService extends Disposable implements IConfigurationServic
private cachedFolderConfigs: ResourceMap<FolderConfiguration>;
private workspaceEditingQueue: Queue<void>;
readonly userSettingsResource: URI;
private readonly configurationFileService: ConfigurationFileService;
protected readonly _onDidChangeConfiguration: Emitter<IConfigurationChangeEvent> = this._register(new Emitter<IConfigurationChangeEvent>());
......@@ -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<ConfigurationModel>(), this.workspace);
this.cachedFolderConfigs = new ResourceMap<FolderConfiguration>();
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));
......
......@@ -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<ITextModel> {
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<LanguageIdentifier>().event, dispose: () => { } } : null);
const model = this.modelService.createModel(content, languageIdentifier ? { languageIdentifier, onDidChange: new Emitter<LanguageIdentifier>().event, dispose: () => { } } : null, this.configurationService.userSettingsResource.with({ scheme: Schemas.vscode }));
this._register(toDisposable(() => {
model.dispose();
this.modelService.destroyModel(model.uri);
......
......@@ -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', () => {
......
......@@ -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<IConfigurationRegistry>(ConfigurationExtensions.Configuration);
suiteSetup(() => {
......@@ -1073,14 +1074,16 @@ suite('WorkspaceConfigurationService-Multiroot', () => {
.then(({ parentDir, configPath }) => {
parentResource = parentDir;
globalSettingsFile = path.join(parentDir, 'settings.json');
const instantiationService = <TestInstantiationService>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');
......
......@@ -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<T>(key: string, overrides?: IConfigurationOverrides): any { return { value: getConfigurationValue<T>(this.getValue(), key), default: getConfigurationValue<T>(this.getValue(), key), user: getConfigurationValue<T>(this.getValue(), key), workspaceFolder: undefined, folder: undefined }; }
public keys() { return { default: [], user: [], workspace: [], workspaceFolder: [] }; }
......
......@@ -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;
......
......@@ -66,7 +66,7 @@ suite('KeybindingsEditing', () => {
instantiationService = new TestInstantiationService();
instantiationService.stub(IEnvironmentService, <IEnvironmentService>{ keybindingsResource: URI.file(keybindingsFile), settingsResource: URI.file(path.join(testDir, 'settings.json')) });
instantiationService.stub(IEnvironmentService, <IEnvironmentService>{ keybindingsResource: URI.file(keybindingsFile) });
instantiationService.stub(IConfigurationService, ConfigurationService);
instantiationService.stub(IConfigurationService, 'getValue', { 'eol': '\n' });
instantiationService.stub(IConfigurationService, 'onDidUpdateConfiguration', () => { });
......
......@@ -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;
......
......@@ -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';
}
......
......@@ -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);
......
......@@ -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<string> {
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<void> {
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 {
......
/*---------------------------------------------------------------------------------------------
* 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<void> = Event.None;
private readonly _onDidChangeFile: Emitter<IFileChange[]> = this._register(new Emitter<IFileChange[]>());
readonly onDidChangeFile: Event<IFileChange[]> = this._onDidChangeFile.event;
private versions: Map<string, number> = new Map<string, number>();
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<IStat> {
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<void> { throw new Error('not supported'); }
readdir(resource: URI): Promise<[string, FileType][]> { throw new Error('not supported'); }
delete(resource: URI, opts: FileDeleteOptions): 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 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<void> {
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
......@@ -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<IUserDataService>('userDataService');
export interface IUserDataChangesEvent {
......@@ -18,6 +20,10 @@ export interface IUserDataService {
onDidChange: Event<IUserDataChangesEvent>;
toResource(key: string): URI;
toKey(resource: URI): string | undefined;
read(key: string): Promise<string>;
write(key: string, value: string): Promise<void>;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册