From bed2921b47f8ceb4fad8d8afc1597557aec3337e Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Fri, 22 Mar 2019 16:51:46 +0100 Subject: [PATCH] Support remote user configuration --- .../configuration/common/configuration.ts | 6 + .../common/configurationModels.ts | 62 +++++-- .../node/configurationService.ts | 6 +- .../test/common/testConfigurationService.ts | 2 + .../api/node/extHostConfiguration.ts | 2 +- src/vs/workbench/browser/contextkeys.ts | 4 +- src/vs/workbench/common/contextkeys.ts | 1 + .../preferences/browser/preferencesActions.ts | 18 +++ .../preferences.contribution.ts | 16 +- src/vs/workbench/electron-browser/main.ts | 26 +-- .../common/configurationEditingService.ts | 27 +++- .../common/configurationModels.ts | 26 ++- .../configuration/node/configuration.ts | 152 +++++++++++++++++- .../node/configurationService.ts | 61 +++++-- .../test/common/configurationModels.test.ts | 2 +- .../configurationEditingService.test.ts | 7 +- .../configurationService.test.ts | 32 ++-- .../preferences/browser/preferencesService.ts | 13 +- .../preferences/common/preferences.ts | 1 + 19 files changed, 389 insertions(+), 75 deletions(-) diff --git a/src/vs/platform/configuration/common/configuration.ts b/src/vs/platform/configuration/common/configuration.ts index e6f20a487da..8fef2a4b7db 100644 --- a/src/vs/platform/configuration/common/configuration.ts +++ b/src/vs/platform/configuration/common/configuration.ts @@ -29,6 +29,8 @@ export interface IConfigurationOverrides { export const enum ConfigurationTarget { USER = 1, + USER_LOCAL, + USER_REMOTE, WORKSPACE, WORKSPACE_FOLDER, DEFAULT, @@ -37,6 +39,8 @@ export const enum ConfigurationTarget { export function ConfigurationTargetToString(configurationTarget: ConfigurationTarget) { switch (configurationTarget) { case ConfigurationTarget.USER: return 'USER'; + case ConfigurationTarget.USER_LOCAL: return 'USER_LOCAL'; + case ConfigurationTarget.USER_REMOTE: return 'USER_REMOTE'; case ConfigurationTarget.WORKSPACE: return 'WORKSPACE'; case ConfigurationTarget.WORKSPACE_FOLDER: return 'WORKSPACE_FOLDER'; case ConfigurationTarget.DEFAULT: return 'DEFAULT'; @@ -88,6 +92,8 @@ export interface IConfigurationService { inspect(key: string, overrides?: IConfigurationOverrides): { default: T, user: T, + userLocal?: T, + userRemote?: T, workspace?: T, workspaceFolder?: T, memory?: T, diff --git a/src/vs/platform/configuration/common/configurationModels.ts b/src/vs/platform/configuration/common/configurationModels.ts index 6eaf03b5eff..ee7e320b6e6 100644 --- a/src/vs/platform/configuration/common/configurationModels.ts +++ b/src/vs/platform/configuration/common/configurationModels.ts @@ -36,6 +36,10 @@ export class ConfigurationModel implements IConfigurationModel { return this.checkAndFreeze(this._keys); } + isEmpty(): boolean { + return this._keys.length === 0 || Object.keys(this._contents).length === 0 || this._overrides.length === 0; + } + getValue(section: string | undefined): V { return section ? getConfigurationValue(this.contents, section) : this.contents; } @@ -283,7 +287,8 @@ export class Configuration { constructor( private _defaultConfiguration: ConfigurationModel, - private _userConfiguration: ConfigurationModel, + private _localUserConfiguration: ConfigurationModel, + private _remoteUserConfiguration: ConfigurationModel = new ConfigurationModel(), private _workspaceConfiguration: ConfigurationModel = new ConfigurationModel(), private _folderConfigurations: ResourceMap = new ResourceMap(), private _memoryConfiguration: ConfigurationModel = new ConfigurationModel(), @@ -322,6 +327,8 @@ export class Configuration { inspect(key: string, overrides: IConfigurationOverrides, workspace: Workspace | undefined): { default: C, user: C, + userLocal?: C, + userRemote?: C, workspace?: C, workspaceFolder?: C memory?: C @@ -332,7 +339,9 @@ export class Configuration { const memoryConfigurationModel = overrides.resource ? this._memoryConfigurationByResource.get(overrides.resource) || this._memoryConfiguration : this._memoryConfiguration; return { default: overrides.overrideIdentifier ? this._defaultConfiguration.freeze().override(overrides.overrideIdentifier).getValue(key) : this._defaultConfiguration.freeze().getValue(key), - user: overrides.overrideIdentifier ? this._userConfiguration.freeze().override(overrides.overrideIdentifier).getValue(key) : this._userConfiguration.freeze().getValue(key), + user: overrides.overrideIdentifier ? this.userConfiguration.freeze().override(overrides.overrideIdentifier).getValue(key) : this.userConfiguration.freeze().getValue(key), + userLocal: overrides.overrideIdentifier ? this.localUserConfiguration.freeze().override(overrides.overrideIdentifier).getValue(key) : this.localUserConfiguration.freeze().getValue(key), + userRemote: overrides.overrideIdentifier ? this.remoteUserConfiguration.freeze().override(overrides.overrideIdentifier).getValue(key) : this.remoteUserConfiguration.freeze().getValue(key), workspace: workspace ? overrides.overrideIdentifier ? this._workspaceConfiguration.freeze().override(overrides.overrideIdentifier).getValue(key) : this._workspaceConfiguration.freeze().getValue(key) : undefined, //Check on workspace exists or not because _workspaceConfiguration is never null workspaceFolder: folderConfigurationModel ? overrides.overrideIdentifier ? folderConfigurationModel.freeze().override(overrides.overrideIdentifier).getValue(key) : folderConfigurationModel.freeze().getValue(key) : undefined, memory: overrides.overrideIdentifier ? memoryConfigurationModel.override(overrides.overrideIdentifier).getValue(key) : memoryConfigurationModel.getValue(key), @@ -349,7 +358,7 @@ export class Configuration { const folderConfigurationModel = this.getFolderConfigurationModelForResource(undefined, workspace); return { default: this._defaultConfiguration.freeze().keys, - user: this._userConfiguration.freeze().keys, + user: this.userConfiguration.freeze().keys, workspace: this._workspaceConfiguration.freeze().keys, workspaceFolder: folderConfigurationModel ? folderConfigurationModel.freeze().keys : [] }; @@ -361,8 +370,16 @@ export class Configuration { this._foldersConsolidatedConfigurations.clear(); } - updateUserConfiguration(userConfiguration: ConfigurationModel): void { - this._userConfiguration = userConfiguration; + updateLocalUserConfiguration(localUserConfiguration: ConfigurationModel): void { + this._localUserConfiguration = localUserConfiguration; + this._userConfiguration = null; + this._workspaceConsolidatedConfiguration = null; + this._foldersConsolidatedConfigurations.clear(); + } + + updateRemoteUserConfiguration(remoteUserConfiguration: ConfigurationModel): void { + this._remoteUserConfiguration = remoteUserConfiguration; + this._userConfiguration = null; this._workspaceConsolidatedConfiguration = null; this._foldersConsolidatedConfigurations.clear(); } @@ -379,7 +396,7 @@ export class Configuration { } deleteFolderConfiguration(resource: URI): void { - this.folders.delete(resource); + this.folderConfigurations.delete(resource); this._foldersConsolidatedConfigurations.delete(resource); } @@ -387,15 +404,30 @@ export class Configuration { return this._defaultConfiguration; } - get user(): ConfigurationModel { + private _userConfiguration: ConfigurationModel | null; + get userConfiguration(): ConfigurationModel { + if (!this._userConfiguration) { + this._userConfiguration = this._remoteUserConfiguration.isEmpty() ? this._localUserConfiguration : this._localUserConfiguration.merge(this._remoteUserConfiguration); + if (this._freeze) { + this._userConfiguration.freeze(); + } + } return this._userConfiguration; } - get workspace(): ConfigurationModel { + get localUserConfiguration(): ConfigurationModel { + return this._localUserConfiguration; + } + + get remoteUserConfiguration(): ConfigurationModel { + return this._remoteUserConfiguration; + } + + get workspaceConfiguration(): ConfigurationModel { return this._workspaceConfiguration; } - protected get folders(): ResourceMap { + protected get folderConfigurations(): ResourceMap { return this._folderConfigurations; } @@ -423,7 +455,7 @@ export class Configuration { private getWorkspaceConsolidatedConfiguration(): ConfigurationModel { if (!this._workspaceConsolidatedConfiguration) { - this._workspaceConsolidatedConfiguration = this._defaultConfiguration.merge(this._userConfiguration, this._workspaceConfiguration, this._memoryConfiguration); + this._workspaceConsolidatedConfiguration = this._defaultConfiguration.merge(this.userConfiguration, this._workspaceConfiguration, this._memoryConfiguration); if (this._freeze) { this._workspaceConfiguration = this._workspaceConfiguration.freeze(); } @@ -467,9 +499,9 @@ export class Configuration { keys: this._defaultConfiguration.keys }, user: { - contents: this._userConfiguration.contents, - overrides: this._userConfiguration.overrides, - keys: this._userConfiguration.keys + contents: this.userConfiguration.contents, + overrides: this.userConfiguration.overrides, + keys: this.userConfiguration.keys }, workspace: { contents: this._workspaceConfiguration.contents, @@ -497,8 +529,8 @@ export class Configuration { }; addKeys(keys.user); addKeys(keys.workspace); - for (const resource of this.folders.keys()) { - addKeys(this.folders.get(resource)!.keys); + for (const resource of this.folderConfigurations.keys()) { + addKeys(this.folderConfigurations.get(resource)!.keys); } return all; } diff --git a/src/vs/platform/configuration/node/configurationService.ts b/src/vs/platform/configuration/node/configurationService.ts index c3d82139cb5..21d827ec0f5 100644 --- a/src/vs/platform/configuration/node/configurationService.ts +++ b/src/vs/platform/configuration/node/configurationService.ts @@ -91,10 +91,10 @@ export class ConfigurationService extends Disposable implements IConfigurationSe } private onDidChangeUserConfiguration(userConfigurationModel: ConfigurationModel): void { - const { added, updated, removed } = compare(this._configuration.user, userConfigurationModel); + const { added, updated, removed } = compare(this._configuration.localUserConfiguration, userConfigurationModel); const changedKeys = [...added, ...updated, ...removed]; if (changedKeys.length) { - this._configuration.updateUserConfiguration(userConfigurationModel); + this._configuration.updateLocalUserConfiguration(userConfigurationModel); this.trigger(changedKeys, ConfigurationTarget.USER); } } @@ -113,7 +113,7 @@ export class ConfigurationService extends Disposable implements IConfigurationSe case ConfigurationTarget.DEFAULT: return this._configuration.defaults.contents; case ConfigurationTarget.USER: - return this._configuration.user.contents; + return this._configuration.localUserConfiguration.contents; } return {}; } diff --git a/src/vs/platform/configuration/test/common/testConfigurationService.ts b/src/vs/platform/configuration/test/common/testConfigurationService.ts index 21dd4c609ee..9bb4c8e2208 100644 --- a/src/vs/platform/configuration/test/common/testConfigurationService.ts +++ b/src/vs/platform/configuration/test/common/testConfigurationService.ts @@ -56,6 +56,8 @@ export class TestConfigurationService implements IConfigurationService { public inspect(key: string, overrides?: IConfigurationOverrides): { default: T, user: T, + userLocal?: T, + userRemote?: T, workspace?: T, workspaceFolder?: T value: T, diff --git a/src/vs/workbench/api/node/extHostConfiguration.ts b/src/vs/workbench/api/node/extHostConfiguration.ts index c68dc36a39b..e36dd7931b9 100644 --- a/src/vs/workbench/api/node/extHostConfiguration.ts +++ b/src/vs/workbench/api/node/extHostConfiguration.ts @@ -263,7 +263,7 @@ export class ExtHostConfigProvider { result.set(URI.parse(key), ExtHostConfigProvider.parseConfigurationModel(data.folders[key])); return result; }, new ResourceMap()); - return new Configuration(defaultConfiguration, userConfiguration, workspaceConfiguration, folders, new ConfigurationModel(), new ResourceMap(), false); + return new Configuration(defaultConfiguration, userConfiguration, new ConfigurationModel(), workspaceConfiguration, folders, new ConfigurationModel(), new ResourceMap(), false); } private static parseConfigurationModel(model: IConfigurationModel): ConfigurationModel { diff --git a/src/vs/workbench/browser/contextkeys.ts b/src/vs/workbench/browser/contextkeys.ts index d33897588ce..9b733922701 100644 --- a/src/vs/workbench/browser/contextkeys.ts +++ b/src/vs/workbench/browser/contextkeys.ts @@ -9,7 +9,7 @@ import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/c import { InputFocusedContext } from 'vs/platform/contextkey/common/contextkeys'; import { IWindowConfiguration, IWindowService } from 'vs/platform/windows/common/windows'; import { ActiveEditorContext, EditorsVisibleContext, TextCompareEditorVisibleContext, TextCompareEditorActiveContext, ActiveEditorGroupEmptyContext, MultipleEditorGroupsContext, TEXT_DIFF_EDITOR_ID, SplitEditorsVertically, InEditorZenModeContext } from 'vs/workbench/common/editor'; -import { IsMacContext, IsLinuxContext, IsWindowsContext, HasMacNativeTabsContext, IsDevelopmentContext, SupportsWorkspacesContext, SupportsOpenFileFolderContext, WorkbenchStateContext, WorkspaceFolderCountContext } from 'vs/workbench/common/contextkeys'; +import { IsMacContext, IsLinuxContext, IsWindowsContext, HasMacNativeTabsContext, IsDevelopmentContext, SupportsWorkspacesContext, SupportsOpenFileFolderContext, WorkbenchStateContext, WorkspaceFolderCountContext, IsRemoteContext } from 'vs/workbench/common/contextkeys'; import { trackFocus, addDisposableListener, EventType } from 'vs/base/browser/dom'; import { preferredSideBySideGroupDirection, GroupDirection, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -90,6 +90,8 @@ export class WorkbenchContextKeysHandler extends Disposable { IsLinuxContext.bindTo(this.contextKeyService); IsWindowsContext.bindTo(this.contextKeyService); + IsRemoteContext.bindTo(this.contextKeyService).set(!!this.windowService.getConfiguration().remoteAuthority); + // macOS Native Tabs const windowConfig = this.configurationService.getValue(); HasMacNativeTabsContext.bindTo(this.contextKeyService).set(windowConfig && windowConfig.window && windowConfig.window.nativeTabs); diff --git a/src/vs/workbench/common/contextkeys.ts b/src/vs/workbench/common/contextkeys.ts index a0921a04c80..8138b46f0cf 100644 --- a/src/vs/workbench/common/contextkeys.ts +++ b/src/vs/workbench/common/contextkeys.ts @@ -9,6 +9,7 @@ import { isMacintosh, isLinux, isWindows } from 'vs/base/common/platform'; export const IsMacContext = new RawContextKey('isMac', isMacintosh); export const IsLinuxContext = new RawContextKey('isLinux', isLinux); export const IsWindowsContext = new RawContextKey('isWindows', isWindows); +export const IsRemoteContext = new RawContextKey('isRemote', false); export const HasMacNativeTabsContext = new RawContextKey('hasMacNativeTabs', false); diff --git a/src/vs/workbench/contrib/preferences/browser/preferencesActions.ts b/src/vs/workbench/contrib/preferences/browser/preferencesActions.ts index 1012138c26d..ab046bf8a33 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferencesActions.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferencesActions.ts @@ -88,6 +88,24 @@ export class OpenGlobalSettingsAction extends Action { } } +export class OpenRemoteSettingsAction extends Action { + + static readonly ID = 'workbench.action.openRemoteSettings'; + static readonly LABEL = nls.localize('openRemoteSettings', "Open User Settings (Remote)"); + + constructor( + id: string, + label: string, + @IPreferencesService private readonly preferencesService: IPreferencesService, + ) { + super(id, label); + } + + run(event?: any): Promise { + return this.preferencesService.openRemoteSettings(); + } +} + export class OpenGlobalKeybindingsAction extends Action { static readonly ID = 'workbench.action.openGlobalKeybindings'; diff --git a/src/vs/workbench/contrib/preferences/electron-browser/preferences.contribution.ts b/src/vs/workbench/contrib/preferences/electron-browser/preferences.contribution.ts index f09b1ee5ce4..b04c52d6aa1 100644 --- a/src/vs/workbench/contrib/preferences/electron-browser/preferences.contribution.ts +++ b/src/vs/workbench/contrib/preferences/electron-browser/preferences.contribution.ts @@ -13,7 +13,7 @@ import * as nls from 'vs/nls'; import { MenuId, MenuRegistry, SyncActionDescriptor } from 'vs/platform/actions/common/actions'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; -import { WorkbenchStateContext } from 'vs/workbench/common/contextkeys'; +import { WorkbenchStateContext, IsRemoteContext } from 'vs/workbench/common/contextkeys'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; @@ -28,7 +28,7 @@ import { Extensions as WorkbenchExtensions, IWorkbenchContribution, IWorkbenchCo import { EditorInput, Extensions as EditorInputExtensions, IEditorInputFactory, IEditorInputFactoryRegistry } from 'vs/workbench/common/editor'; import { ResourceContextKey } from 'vs/workbench/common/resources'; import { KeybindingsEditor } from 'vs/workbench/contrib/preferences/browser/keybindingsEditor'; -import { ConfigureLanguageBasedSettingsAction, OpenDefaultKeybindingsFileAction, OpenFolderSettingsAction, OpenGlobalKeybindingsAction, OpenGlobalKeybindingsFileAction, OpenGlobalSettingsAction, OpenRawDefaultSettingsAction, OpenSettings2Action, OpenSettingsJsonAction, OpenWorkspaceSettingsAction, OPEN_FOLDER_SETTINGS_COMMAND, OPEN_FOLDER_SETTINGS_LABEL } from 'vs/workbench/contrib/preferences/browser/preferencesActions'; +import { ConfigureLanguageBasedSettingsAction, OpenDefaultKeybindingsFileAction, OpenFolderSettingsAction, OpenGlobalKeybindingsAction, OpenGlobalKeybindingsFileAction, OpenGlobalSettingsAction, OpenRawDefaultSettingsAction, OpenSettings2Action, OpenSettingsJsonAction, OpenWorkspaceSettingsAction, OPEN_FOLDER_SETTINGS_COMMAND, OPEN_FOLDER_SETTINGS_LABEL, OpenRemoteSettingsAction } from 'vs/workbench/contrib/preferences/browser/preferencesActions'; import { PreferencesEditor } from 'vs/workbench/contrib/preferences/browser/preferencesEditor'; import { CONTEXT_KEYBINDINGS_EDITOR, CONTEXT_KEYBINDINGS_SEARCH_FOCUS, CONTEXT_KEYBINDING_FOCUS, CONTEXT_SETTINGS_EDITOR, CONTEXT_SETTINGS_JSON_EDITOR, CONTEXT_SETTINGS_SEARCH_FOCUS, CONTEXT_TOC_ROW_FOCUS, IKeybindingsEditor, IPreferencesSearchService, KEYBINDINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, KEYBINDINGS_EDITOR_COMMAND_COPY, KEYBINDINGS_EDITOR_COMMAND_COPY_COMMAND, KEYBINDINGS_EDITOR_COMMAND_DEFINE, KEYBINDINGS_EDITOR_COMMAND_FOCUS_KEYBINDINGS, KEYBINDINGS_EDITOR_COMMAND_RECORD_SEARCH_KEYS, KEYBINDINGS_EDITOR_COMMAND_REMOVE, KEYBINDINGS_EDITOR_COMMAND_RESET, KEYBINDINGS_EDITOR_COMMAND_SEARCH, KEYBINDINGS_EDITOR_COMMAND_SHOW_SIMILAR, KEYBINDINGS_EDITOR_COMMAND_SORTBY_PRECEDENCE, KEYBINDINGS_EDITOR_SHOW_DEFAULT_KEYBINDINGS, KEYBINDINGS_EDITOR_SHOW_USER_KEYBINDINGS, MODIFIED_SETTING_TAG, SETTINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, SETTINGS_EDITOR_COMMAND_EDIT_FOCUSED_SETTING, SETTINGS_EDITOR_COMMAND_FILTER_MODIFIED, SETTINGS_EDITOR_COMMAND_FILTER_ONLINE, SETTINGS_EDITOR_COMMAND_FOCUS_FILE, SETTINGS_EDITOR_COMMAND_FOCUS_NEXT_SETTING, SETTINGS_EDITOR_COMMAND_FOCUS_PREVIOUS_SETTING, SETTINGS_EDITOR_COMMAND_FOCUS_SETTINGS_FROM_SEARCH, SETTINGS_EDITOR_COMMAND_FOCUS_SETTINGS_LIST, SETTINGS_EDITOR_COMMAND_SEARCH, SETTINGS_EDITOR_COMMAND_SHOW_CONTEXT_MENU, SETTINGS_EDITOR_COMMAND_SWITCH_TO_JSON, SETTINGS_COMMAND_OPEN_SETTINGS, KEYBINDINGS_EDITOR_COMMAND_DEFINE_WHEN } from 'vs/workbench/contrib/preferences/common/preferences'; import { PreferencesContribution } from 'vs/workbench/contrib/preferences/common/preferencesContribution'; @@ -553,6 +553,18 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, { group: '1_keyboard_preferences_actions' }); +CommandsRegistry.registerCommand(OpenRemoteSettingsAction.ID, serviceAccessor => { + serviceAccessor.get(IInstantiationService).createInstance(OpenRemoteSettingsAction, OpenRemoteSettingsAction.ID, OpenRemoteSettingsAction.LABEL).run(); +}); +MenuRegistry.appendMenuItem(MenuId.CommandPalette, { + command: { + id: OpenRemoteSettingsAction.ID, + title: { value: OpenRemoteSettingsAction.LABEL, original: 'Preferences: Open Remote Settings' }, + category: nls.localize('preferencesCategory', "Preferences") + }, + when: IsRemoteContext +}); + abstract class SettingsCommand extends Command { protected getPreferencesEditor(accessor: ServicesAccessor): PreferencesEditor | SettingsEditor2 | null { diff --git a/src/vs/workbench/electron-browser/main.ts b/src/vs/workbench/electron-browser/main.ts index 9be40d83671..806e2b25856 100644 --- a/src/vs/workbench/electron-browser/main.ts +++ b/src/vs/workbench/electron-browser/main.ts @@ -12,7 +12,7 @@ import { ElectronWindow } from 'vs/workbench/electron-browser/window'; import { setZoomLevel, setZoomFactor, setFullscreen } from 'vs/base/browser/browser'; import { domContentLoaded, addDisposableListener, EventType, scheduleAtNextAnimationFrame } from 'vs/base/browser/dom'; import { onUnexpectedError } from 'vs/base/common/errors'; -import { isLinux, isMacintosh, isWindows } from 'vs/base/common/platform'; +import { isLinux, isMacintosh, isWindows, OperatingSystem } from 'vs/base/common/platform'; import { URI as uri } from 'vs/base/common/uri'; import { WorkspaceService, DefaultConfigurationExportHelper } from 'vs/workbench/services/configuration/node/configurationService'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; @@ -46,6 +46,8 @@ import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteA import { FileService2 } from 'vs/workbench/services/files2/common/fileService2'; import { IFileService } from 'vs/platform/files/common/files'; import { DiskFileSystemProvider } from 'vs/workbench/services/files2/node/diskFileSystemProvider'; +import { IChannel } from 'vs/base/parts/ipc/common/ipc'; +import { REMOTE_FILE_SYSTEM_CHANNEL_NAME, RemoteExtensionsFileSystemProvider } from 'vs/platform/remote/node/remoteAgentFileSystemChannel'; class CodeRendererMain extends Disposable { @@ -160,11 +162,6 @@ class CodeRendererMain extends Disposable { private initServices(): Promise<{ serviceCollection: ServiceCollection, logService: ILogService, storageService: StorageService }> { const serviceCollection = new ServiceCollection(); - // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - // NOTE: DO NOT ADD ANY OTHER SERVICE INTO THE COLLECTION HERE. - // CONTRIBUTE IT VIA WORKBENCH.MAIN.TS AND registerSingleton(). - // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - // Main Process const mainProcessService = this._register(new MainProcessService(this.configuration.windowId)); serviceCollection.set(IMainProcessService, mainProcessService); @@ -192,8 +189,19 @@ class CodeRendererMain extends Disposable { const remoteAgentService = new RemoteAgentService(this.configuration, environmentService, remoteAuthorityResolverService); serviceCollection.set(IRemoteAgentService, remoteAgentService); + const connection = remoteAgentService.getConnection(); + if (connection) { + const channel = connection.getChannel(REMOTE_FILE_SYSTEM_CHANNEL_NAME); + const fileSystemProvider = new RemoteExtensionsFileSystemProvider(channel); + fileService.registerProvider('vscode-remote', fileSystemProvider); + remoteAgentService.getEnvironment().then(remoteAgentEnvironment => { + const isCaseSensitive = !!(remoteAgentEnvironment && remoteAgentEnvironment.os === OperatingSystem.Linux); + fileSystemProvider.setCaseSensitive(isCaseSensitive); + }); + } + return this.resolveWorkspaceInitializationPayload(environmentService).then(payload => Promise.all([ - this.createWorkspaceService(payload, environmentService, logService).then(service => { + this.createWorkspaceService(payload, environmentService, remoteAgentService, logService).then(service => { // Workspace serviceCollection.set(IWorkspaceContextService, service); @@ -284,8 +292,8 @@ class CodeRendererMain extends Disposable { }, error => onUnexpectedError(error)); } - private createWorkspaceService(payload: IWorkspaceInitializationPayload, environmentService: IEnvironmentService, logService: ILogService): Promise { - const workspaceService = new WorkspaceService(environmentService); + private createWorkspaceService(payload: IWorkspaceInitializationPayload, environmentService: IEnvironmentService, remoteAgentService: IRemoteAgentService, logService: ILogService): Promise { + const workspaceService = new WorkspaceService(this.configuration, environmentService, remoteAgentService); return workspaceService.initialize(payload).then(() => workspaceService, error => { onUnexpectedError(error); diff --git a/src/vs/workbench/services/configuration/common/configurationEditingService.ts b/src/vs/workbench/services/configuration/common/configurationEditingService.ts index 4d9c769f0a0..73b33af283e 100644 --- a/src/vs/workbench/services/configuration/common/configurationEditingService.ts +++ b/src/vs/workbench/services/configuration/common/configurationEditingService.ts @@ -28,6 +28,7 @@ import { ITextModel } from 'vs/editor/common/model'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; import { withUndefinedAsNull, withNullAsUndefined } from 'vs/base/common/types'; +import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; export const enum ConfigurationEditingErrorCode { @@ -120,6 +121,7 @@ export class ConfigurationEditingService { public _serviceBrand: any; private queue: Queue; + private remoteSettingsResource: URI | null; constructor( @IConfigurationService private readonly configurationService: IConfigurationService, @@ -130,9 +132,15 @@ export class ConfigurationEditingService { @ITextFileService private readonly textFileService: ITextFileService, @INotificationService private readonly notificationService: INotificationService, @IPreferencesService private readonly preferencesService: IPreferencesService, - @IEditorService private readonly editorService: IEditorService + @IEditorService private readonly editorService: IEditorService, + @IRemoteAgentService remoteAgentService: IRemoteAgentService ) { this.queue = new Queue(); + remoteAgentService.getEnvironment().then(environment => { + if (environment) { + this.remoteSettingsResource = environment.appSettingsPath; + } + }); } writeConfiguration(target: ConfigurationTarget, value: IConfigurationValue, options: IConfigurationEditingOptions = {}): Promise { @@ -458,7 +466,7 @@ export class ConfigurationEditingService { if (config.key) { const standaloneConfigurationKeys = Object.keys(WORKSPACE_STANDALONE_CONFIGURATIONS); for (const key of standaloneConfigurationKeys) { - const resource = this.getConfigurationFileResource(target, WORKSPACE_STANDALONE_CONFIGURATIONS[key], overrides.resource); + const resource = this.getConfigurationFileResource(target, config, WORKSPACE_STANDALONE_CONFIGURATIONS[key], overrides.resource); // Check for prefix if (config.key === key) { @@ -478,10 +486,10 @@ export class ConfigurationEditingService { let key = config.key; let jsonPath = overrides.overrideIdentifier ? [keyFromOverrideIdentifier(overrides.overrideIdentifier), key] : [key]; if (target === ConfigurationTarget.USER) { - return { key, jsonPath, value: config.value, resource: URI.file(this.environmentService.appSettingsPath), target }; + return { key, jsonPath, value: config.value, resource: withNullAsUndefined(this.getConfigurationFileResource(target, config, '', null)), target }; } - const resource = this.getConfigurationFileResource(target, FOLDER_SETTINGS_PATH, overrides.resource); + const resource = this.getConfigurationFileResource(target, config, FOLDER_SETTINGS_PATH, overrides.resource); if (this.isWorkspaceConfigurationResource(resource)) { jsonPath = ['settings', ...jsonPath]; } @@ -493,8 +501,17 @@ export class ConfigurationEditingService { return !!(workspace.configuration && resource && workspace.configuration.fsPath === resource.fsPath); } - private getConfigurationFileResource(target: ConfigurationTarget, relativePath: string, resource: URI | null | undefined): URI | null { + private getConfigurationFileResource(target: ConfigurationTarget, config: IConfigurationValue, relativePath: string, resource: URI | null | undefined): URI | null { + if (target === ConfigurationTarget.USER_LOCAL) { + return URI.file(this.environmentService.appSettingsPath); + } + if (target === ConfigurationTarget.USER_REMOTE) { + return this.remoteSettingsResource; + } if (target === ConfigurationTarget.USER) { + if (this.configurationService.inspect(config.key).userRemote !== undefined) { + return this.remoteSettingsResource; + } return URI.file(this.environmentService.appSettingsPath); } diff --git a/src/vs/workbench/services/configuration/common/configurationModels.ts b/src/vs/workbench/services/configuration/common/configurationModels.ts index 6644ee75e04..a0dc8f9c1ad 100644 --- a/src/vs/workbench/services/configuration/common/configurationModels.ts +++ b/src/vs/workbench/services/configuration/common/configurationModels.ts @@ -131,13 +131,14 @@ export class Configuration extends BaseConfiguration { constructor( defaults: ConfigurationModel, - user: ConfigurationModel, + localUser: ConfigurationModel, + remoteUser: ConfigurationModel, workspaceConfiguration: ConfigurationModel, folders: ResourceMap, memoryConfiguration: ConfigurationModel, memoryConfigurationByResource: ResourceMap, private readonly _workspace?: Workspace) { - super(defaults, user, workspaceConfiguration, folders, memoryConfiguration, memoryConfigurationByResource); + super(defaults, localUser, remoteUser, workspaceConfiguration, folders, memoryConfiguration, memoryConfigurationByResource); } getValue(key: string | undefined, overrides: IConfigurationOverrides = {}): any { @@ -164,17 +165,26 @@ export class Configuration extends BaseConfiguration { return super.keys(this._workspace); } - compareAndUpdateUserConfiguration(user: ConfigurationModel): ConfigurationChangeEvent { - const { added, updated, removed } = compare(this.user, user); + compareAndUpdateLocalUserConfiguration(user: ConfigurationModel): ConfigurationChangeEvent { + const { added, updated, removed } = compare(this.localUserConfiguration, user); let changedKeys = [...added, ...updated, ...removed]; if (changedKeys.length) { - super.updateUserConfiguration(user); + super.updateLocalUserConfiguration(user); + } + return new ConfigurationChangeEvent().change(changedKeys); + } + + compareAndUpdateRemoteUserConfiguration(user: ConfigurationModel): ConfigurationChangeEvent { + const { added, updated, removed } = compare(this.remoteUserConfiguration, user); + let changedKeys = [...added, ...updated, ...removed]; + if (changedKeys.length) { + super.updateRemoteUserConfiguration(user); } return new ConfigurationChangeEvent().change(changedKeys); } compareAndUpdateWorkspaceConfiguration(workspaceConfiguration: ConfigurationModel): ConfigurationChangeEvent { - const { added, updated, removed } = compare(this.workspace, workspaceConfiguration); + const { added, updated, removed } = compare(this.workspaceConfiguration, workspaceConfiguration); let changedKeys = [...added, ...updated, ...removed]; if (changedKeys.length) { super.updateWorkspaceConfiguration(workspaceConfiguration); @@ -183,7 +193,7 @@ export class Configuration extends BaseConfiguration { } compareAndUpdateFolderConfiguration(resource: URI, folderConfiguration: ConfigurationModel): ConfigurationChangeEvent { - const currentFolderConfiguration = this.folders.get(resource); + const currentFolderConfiguration = this.folderConfigurations.get(resource); if (currentFolderConfiguration) { const { added, updated, removed } = compare(currentFolderConfiguration, folderConfiguration); let changedKeys = [...added, ...updated, ...removed]; @@ -202,7 +212,7 @@ export class Configuration extends BaseConfiguration { // Do not remove workspace configuration return new ConfigurationChangeEvent(); } - const folderConfig = this.folders.get(folder); + const folderConfig = this.folderConfigurations.get(folder); if (!folderConfig) { throw new Error('Unknown folder'); } diff --git a/src/vs/workbench/services/configuration/node/configuration.ts b/src/vs/workbench/services/configuration/node/configuration.ts index bf333330d4f..13cf02bc26b 100644 --- a/src/vs/workbench/services/configuration/node/configuration.ts +++ b/src/vs/workbench/services/configuration/node/configuration.ts @@ -13,7 +13,7 @@ import * as collections from 'vs/base/common/collections'; import { Disposable, IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle'; import { RunOnceScheduler, Delayer } from 'vs/base/common/async'; import { FileChangeType, FileChangesEvent, IContent, IFileService } from 'vs/platform/files/common/files'; -import { ConfigurationModel } from 'vs/platform/configuration/common/configurationModels'; +import { ConfigurationModel, ConfigurationModelParser } from 'vs/platform/configuration/common/configurationModels'; import { WorkspaceConfigurationModelParser, FolderSettingsModelParser, StandaloneConfigurationModelParser } from 'vs/workbench/services/configuration/common/configurationModels'; import { FOLDER_SETTINGS_PATH, TASKS_CONFIGURATION_KEY, FOLDER_SETTINGS_NAME, LAUNCH_CONFIGURATION_KEY } from 'vs/workbench/services/configuration/common/configuration'; import { IStoredWorkspaceFolder } from 'vs/platform/workspaces/common/workspaces'; @@ -25,7 +25,155 @@ import { extname, join } from 'vs/base/common/path'; import { equals } from 'vs/base/common/objects'; import { Schemas } from 'vs/base/common/network'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { IConfigurationModel } from 'vs/platform/configuration/common/configuration'; +import { IConfigurationModel, compare } from 'vs/platform/configuration/common/configuration'; + +export class RemoteUserConfiguration extends Disposable { + + private readonly _cachedConfiguration: CachedUserConfiguration; + private _userConfiguration: FileServiceBasedUserConfiguration | CachedUserConfiguration; + + private readonly _onDidUpdateConfiguration: Emitter = this._register(new Emitter()); + public readonly onDidChangeConfiguration: Event = this._onDidUpdateConfiguration.event; + + constructor( + remoteAuthority: string, + environmentService: IEnvironmentService + ) { + super(); + this._userConfiguration = this._cachedConfiguration = new CachedUserConfiguration(remoteAuthority, environmentService); + } + + load(): Promise { + return this._userConfiguration.loadConfiguration(); + } + + async adopt(configurationResource: URI | null, fileService: IFileService): Promise { + if (this._userConfiguration instanceof CachedUserConfiguration) { + const oldConfigurationModel = this._userConfiguration.getConfigurationModel(); + let newConfigurationModel = new ConfigurationModel(); + if (configurationResource) { + this._userConfiguration = new FileServiceBasedUserConfiguration(configurationResource, oldConfigurationModel, fileService); + this._register(this._userConfiguration.onDidChange(configurationModel => this.onDidUserConfigurationChange(configurationModel))); + newConfigurationModel = await this._userConfiguration.loadConfiguration(); + } + const { added, updated, removed } = compare(oldConfigurationModel, newConfigurationModel); + if (added.length > 0 || updated.length > 0 || removed.length > 0) { + this.updateCache(newConfigurationModel); + return newConfigurationModel; + } + } + return null; + } + + private onDidUserConfigurationChange(configurationModel: ConfigurationModel): void { + this.updateCache(configurationModel); + this._onDidUpdateConfiguration.fire(configurationModel); + } + + private updateCache(configurationModel: ConfigurationModel): Promise { + return this._cachedConfiguration.updateConfiguration(configurationModel); + } +} + +class FileServiceBasedUserConfiguration extends Disposable { + + private readonly reloadConfigurationScheduler: RunOnceScheduler; + protected readonly _onDidChange: Emitter = this._register(new Emitter()); + readonly onDidChange: Event = this._onDidChange.event; + + constructor( + private readonly configurationResource: URI, + private configurationModel: ConfigurationModel, + private readonly fileService: IFileService + ) { + super(); + + this._register(fileService.onFileChanges(e => this.handleFileEvents(e))); + this.reloadConfigurationScheduler = this._register(new RunOnceScheduler(() => this.loadConfiguration().then(configurationModel => this._onDidChange.fire(configurationModel)), 50)); + this.fileService.watchFileChanges(this.configurationResource); + this._register(toDisposable(() => this.fileService.unwatchFileChanges(this.configurationResource))); + } + + loadConfiguration(): Promise { + return this.fileService.resolveContent(this.configurationResource) + .then(content => content.value, e => { + errors.onUnexpectedError(e); + return ''; + }).then(content => { + const parser = new ConfigurationModelParser(this.configurationResource.toString()); + parser.parse(content); + this.configurationModel = parser.configurationModel; + return this.configurationModel; + }); + } + + private handleFileEvents(event: FileChangesEvent): void { + const events = event.changes; + + let affectedByChanges = false; + // Find changes that affect workspace file + for (let i = 0, len = events.length; i < len && !affectedByChanges; i++) { + affectedByChanges = resources.isEqual(this.configurationResource, events[i].resource); + } + + if (affectedByChanges) { + this.reloadConfigurationScheduler.schedule(); + } + } +} + +class CachedUserConfiguration extends Disposable { + + private readonly _onDidChange: Emitter = this._register(new Emitter()); + readonly onDidChange: Event = this._onDidChange.event; + + private readonly cachedFolderPath: string; + private readonly cachedConfigurationPath: string; + private configurationModel: ConfigurationModel; + + constructor( + remoteAuthority: string, + private environmentService: IEnvironmentService + ) { + super(); + this.cachedFolderPath = join(this.environmentService.userDataPath, 'CachedConfigurations', 'user', remoteAuthority); + this.cachedConfigurationPath = join(this.cachedFolderPath, 'configuration.json'); + this.configurationModel = new ConfigurationModel(); + } + + getConfigurationModel(): ConfigurationModel { + return this.configurationModel; + } + + loadConfiguration(): Promise { + return pfs.readFile(this.cachedConfigurationPath) + .then(content => content.toString(), () => '') + .then(content => { + try { + const parsed: IConfigurationModel = JSON.parse(content); + this.configurationModel = new ConfigurationModel(parsed.contents, parsed.keys, parsed.overrides); + } catch (e) { + } + return this.configurationModel; + }); + } + + updateConfiguration(configurationModel: ConfigurationModel): Promise { + const raw = JSON.stringify(configurationModel.toJSON()); + return this.createCachedFolder().then(created => { + if (created) { + return configurationModel.keys.length ? pfs.writeFile(this.cachedConfigurationPath, raw) : pfs.rimraf(this.cachedFolderPath); + } + return undefined; + }); + } + + private createCachedFolder(): Promise { + return Promise.resolve(pfs.exists(this.cachedFolderPath)) + .then(undefined, () => false) + .then(exists => exists ? exists : pfs.mkdirp(this.cachedFolderPath).then(() => true, () => false)); + } +} export interface IWorkspaceIdentifier { id: string; diff --git a/src/vs/workbench/services/configuration/node/configurationService.ts b/src/vs/workbench/services/configuration/node/configurationService.ts index 40f6b5da14d..ec6ee63a186 100644 --- a/src/vs/workbench/services/configuration/node/configurationService.ts +++ b/src/vs/workbench/services/configuration/node/configurationService.ts @@ -28,7 +28,7 @@ import { ICommandService } from 'vs/platform/commands/common/commands'; import product from 'vs/platform/product/node/product'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ConfigurationEditingService } from 'vs/workbench/services/configuration/common/configurationEditingService'; -import { WorkspaceConfiguration, FolderConfiguration } from 'vs/workbench/services/configuration/node/configuration'; +import { WorkspaceConfiguration, FolderConfiguration, RemoteUserConfiguration } from 'vs/workbench/services/configuration/node/configuration'; import { JSONEditingService } from 'vs/workbench/services/configuration/common/jsonEditingService'; import { UserConfiguration } from 'vs/platform/configuration/node/configuration'; import { IJSONSchema, IJSONSchemaMap } from 'vs/base/common/jsonSchema'; @@ -36,6 +36,8 @@ import { localize } from 'vs/nls'; import { isEqual, dirname } from 'vs/base/common/resources'; import { mark } from 'vs/base/common/performance'; import { Schemas } from 'vs/base/common/network'; +import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; +import { IWindowConfiguration } from 'vs/platform/windows/common/windows'; export class WorkspaceService extends Disposable implements IConfigurationService, IWorkspaceContextService { @@ -45,7 +47,8 @@ export class WorkspaceService extends Disposable implements IConfigurationServic private completeWorkspaceBarrier: Barrier; private _configuration: Configuration; private defaultConfiguration: DefaultConfigurationModel; - private userConfiguration: UserConfiguration; + private localUserConfiguration: UserConfiguration; + private remoteUserConfiguration: RemoteUserConfiguration | null = null; private workspaceConfiguration: WorkspaceConfiguration; private cachedFolderConfigs: ResourceMap; @@ -67,14 +70,18 @@ export class WorkspaceService extends Disposable implements IConfigurationServic private configurationEditingService: ConfigurationEditingService; private jsonEditingService: JSONEditingService; - constructor(private environmentService: IEnvironmentService, private workspaceSettingsRootFolder: string = FOLDER_CONFIG_FOLDER_NAME) { + constructor(configuration: IWindowConfiguration, private environmentService: IEnvironmentService, private remoteAgentService: IRemoteAgentService, private workspaceSettingsRootFolder: string = FOLDER_CONFIG_FOLDER_NAME) { super(); this.completeWorkspaceBarrier = new Barrier(); this.defaultConfiguration = new DefaultConfigurationModel(); - this.userConfiguration = this._register(new UserConfiguration(environmentService.appSettingsPath)); + this.localUserConfiguration = this._register(new UserConfiguration(environmentService.appSettingsPath)); + if (configuration.remoteAuthority) { + this.remoteUserConfiguration = this._register(new RemoteUserConfiguration(configuration.remoteAuthority, environmentService)); + this._register(this.remoteUserConfiguration.onDidChangeConfiguration(userConfiguration => this.onRemoteUserConfigurationChanged(userConfiguration))); + } this.workspaceConfiguration = this._register(new WorkspaceConfiguration(environmentService)); - this._register(this.userConfiguration.onDidChangeConfiguration(userConfiguration => this.onUserConfigurationChanged(userConfiguration))); + this._register(this.localUserConfiguration.onDidChangeConfiguration(userConfiguration => this.onLocalUserConfigurationChanged(userConfiguration))); this._register(this.workspaceConfiguration.onDidUpdateConfiguration(() => this.onWorkspaceConfigurationChanged())); this._register(Registry.as(Extensions.Configuration).onDidSchemaChange(e => this.registerConfigurationSchemas())); @@ -254,8 +261,8 @@ export class WorkspaceService extends Disposable implements IConfigurationServic return this.reloadWorkspaceFolderConfiguration(folder, key); } return this.reloadUserConfiguration() - .then(userConfigurationModel => this.reloadWorkspaceConfiguration() - .then(() => this.loadConfiguration(userConfigurationModel))); + .then(({ local, remote }) => this.reloadWorkspaceConfiguration() + .then(() => this.loadConfiguration(local, remote))); } inspect(key: string, overrides?: IConfigurationOverrides): { @@ -306,6 +313,15 @@ export class WorkspaceService extends Disposable implements IConfigurationServic } this.releaseWorkspaceBarrier(); }); + if (this.remoteUserConfiguration) { + this.remoteAgentService.getEnvironment() + .then(environment => this.remoteUserConfiguration!.adopt(environment ? environment.appSettingsPath : null, fileService) + .then(changedModel => { + if (changedModel) { + this.onRemoteUserConfigurationChanged(changedModel); + } + })); + } } acquireInstantiationService(instantiationService: IInstantiationService): void { @@ -425,12 +441,18 @@ export class WorkspaceService extends Disposable implements IConfigurationServic private initializeConfiguration(): Promise { this.registerConfigurationSchemas(); - return this.userConfiguration.initialize() - .then(userConfigurationModel => this.loadConfiguration(userConfigurationModel)); + return this.initializeUserConfiguration() + .then(({ local, remote }) => this.loadConfiguration(local, remote)); + } + + private initializeUserConfiguration(): Promise<{ local: ConfigurationModel, remote: ConfigurationModel }> { + return Promise.all([this.localUserConfiguration.initialize(), this.remoteUserConfiguration ? this.remoteUserConfiguration.load() : Promise.resolve(new ConfigurationModel())]) + .then(([local, remote]) => ({ local, remote })); } - private reloadUserConfiguration(key?: string): Promise { - return this.userConfiguration.reload(); + private reloadUserConfiguration(key?: string): Promise<{ local: ConfigurationModel, remote: ConfigurationModel }> { + return Promise.all([this.localUserConfiguration.reload(), this.remoteUserConfiguration ? this.remoteUserConfiguration.load() : Promise.resolve(new ConfigurationModel())]) + .then(([local, remote]) => ({ local, remote })); } private reloadWorkspaceConfiguration(key?: string): Promise { @@ -448,7 +470,7 @@ export class WorkspaceService extends Disposable implements IConfigurationServic return this.onWorkspaceFolderConfigurationChanged(folder, key); } - private loadConfiguration(userConfigurationModel: ConfigurationModel): Promise { + private loadConfiguration(userConfigurationModel: ConfigurationModel, remoteUserConfigurationModel: ConfigurationModel): Promise { // reset caches this.cachedFolderConfigs = new ResourceMap(); @@ -461,7 +483,7 @@ export class WorkspaceService extends Disposable implements IConfigurationServic folderConfigurations.forEach((folderConfiguration, index) => folderConfigurationModels.set(folders[index].uri, folderConfiguration)); const currentConfiguration = this._configuration; - this._configuration = new Configuration(this.defaultConfiguration, userConfigurationModel, workspaceConfiguration, folderConfigurationModels, new ConfigurationModel(), new ResourceMap(), this.workspace); + this._configuration = new Configuration(this.defaultConfiguration, userConfigurationModel, remoteUserConfigurationModel, workspaceConfiguration, folderConfigurationModels, new ConfigurationModel(), new ResourceMap(), this.workspace); if (currentConfiguration) { const changedKeys = this._configuration.compare(currentConfiguration); @@ -528,8 +550,13 @@ export class WorkspaceService extends Disposable implements IConfigurationServic } } - private onUserConfigurationChanged(userConfiguration: ConfigurationModel): void { - const keys = this._configuration.compareAndUpdateUserConfiguration(userConfiguration); + private onLocalUserConfigurationChanged(userConfiguration: ConfigurationModel): void { + const keys = this._configuration.compareAndUpdateLocalUserConfiguration(userConfiguration); + this.triggerConfigurationChange(keys, ConfigurationTarget.USER); + } + + private onRemoteUserConfigurationChanged(userConfiguration: ConfigurationModel): void { + const keys = this._configuration.compareAndUpdateRemoteUserConfiguration(userConfiguration); this.triggerConfigurationChange(keys, ConfigurationTarget.USER); } @@ -670,9 +697,9 @@ export class WorkspaceService extends Disposable implements IConfigurationServic case ConfigurationTarget.DEFAULT: return this._configuration.defaults.contents; case ConfigurationTarget.USER: - return this._configuration.user.contents; + return this._configuration.userConfiguration.contents; case ConfigurationTarget.WORKSPACE: - return this._configuration.workspace.contents; + return this._configuration.workspaceConfiguration.contents; } return {}; } diff --git a/src/vs/workbench/services/configuration/test/common/configurationModels.test.ts b/src/vs/workbench/services/configuration/test/common/configurationModels.test.ts index 49adc81b759..e9cff289e28 100644 --- a/src/vs/workbench/services/configuration/test/common/configurationModels.test.ts +++ b/src/vs/workbench/services/configuration/test/common/configurationModels.test.ts @@ -201,7 +201,7 @@ suite('AllKeysConfigurationChangeEvent', () => { test('changeEvent affects keys for any resource', () => { const configuraiton = new Configuration(new ConfigurationModel({}, ['window.title', 'window.zoomLevel', 'window.restoreFullscreen', 'workbench.editor.enablePreview', 'window.restoreWindows']), - new ConfigurationModel(), new ConfigurationModel(), new ResourceMap(), new ConfigurationModel(), new ResourceMap(), null!); + new ConfigurationModel(), new ConfigurationModel(), new ConfigurationModel(), new ResourceMap(), new ConfigurationModel(), new ResourceMap(), null!); let testObject = new AllKeysConfigurationChangeEvent(configuraiton, ConfigurationTarget.USER, null); assert.deepEqual(testObject.affectedKeys, ['window.title', 'window.zoomLevel', 'window.restoreFullscreen', 'workbench.editor.enablePreview', 'window.restoreWindows']); 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 a4c4ca785f2..08fe8396e52 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 @@ -36,6 +36,9 @@ import { ICommandService } from 'vs/platform/commands/common/commands'; import { CommandService } from 'vs/workbench/services/commands/common/commandService'; import { URI } from 'vs/base/common/uri'; import { createHash } from 'crypto'; +import { IWindowConfiguration } from 'vs/platform/windows/common/windows'; +import { RemoteAgentService } from 'vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl'; +import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; class SettingsTestEnvironmentService extends EnvironmentService { @@ -99,7 +102,9 @@ suite('ConfigurationEditingService', () => { instantiationService = workbenchInstantiationService(); const environmentService = new SettingsTestEnvironmentService(parseArgs(process.argv), process.execPath, globalSettingsFile); instantiationService.stub(IEnvironmentService, environmentService); - const workspaceService = new WorkspaceService(environmentService); + const remoteAgentService = instantiationService.createInstance(RemoteAgentService, {}); + instantiationService.stub(IRemoteAgentService, remoteAgentService); + const workspaceService = new WorkspaceService({}, environmentService, remoteAgentService); instantiationService.stub(IWorkspaceContextService, workspaceService); return workspaceService.initialize(noWorkspace ? { id: '' } : { folder: URI.file(workspaceDir), id: createHash('md5').update(URI.file(workspaceDir).toString()).digest('hex') }).then(() => { instantiationService.stub(IConfigurationService, workspaceService); 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 9c3d4d48c9c..b327fc8ee33 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 @@ -37,6 +37,10 @@ import { Schemas } from 'vs/base/common/network'; import { originalFSPath } from 'vs/base/common/resources'; import { isLinux } from 'vs/base/common/platform'; import { IWorkspaceIdentifier } from 'vs/workbench/services/configuration/node/configuration'; +import { IWindowConfiguration } from 'vs/platform/windows/common/windows'; +import { RemoteAgentService } from 'vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl'; +import { RemoteAuthorityResolverService } from 'vs/platform/remote/electron-browser/remoteAuthorityResolverService'; +import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; class SettingsTestEnvironmentService extends EnvironmentService { @@ -95,7 +99,7 @@ suite('WorkspaceContextService - Folder', () => { workspaceResource = folderDir; const globalSettingsFile = path.join(parentDir, 'settings.json'); const environmentService = new SettingsTestEnvironmentService(parseArgs(process.argv), process.execPath, globalSettingsFile); - workspaceContextService = new WorkspaceService(environmentService); + workspaceContextService = new WorkspaceService({}, environmentService, new RemoteAgentService({}, environmentService, new RemoteAuthorityResolverService())); return (workspaceContextService).initialize(convertToWorkspacePayload(URI.file(folderDir))); }); }); @@ -153,10 +157,12 @@ suite('WorkspaceContextService - Workspace', () => { parentResource = parentDir; + instantiationService = workbenchInstantiationService(); const environmentService = new SettingsTestEnvironmentService(parseArgs(process.argv), process.execPath, path.join(parentDir, 'settings.json')); - const workspaceService = new WorkspaceService(environmentService); + const remoteAgentService = instantiationService.createInstance(RemoteAgentService, {}); + instantiationService.stub(IRemoteAgentService, remoteAgentService); + const workspaceService = new WorkspaceService({}, environmentService, remoteAgentService); - instantiationService = workbenchInstantiationService(); instantiationService.stub(IWorkspaceContextService, workspaceService); instantiationService.stub(IConfigurationService, workspaceService); instantiationService.stub(IEnvironmentService, environmentService); @@ -207,10 +213,12 @@ suite('WorkspaceContextService - Workspace Editing', () => { parentResource = parentDir; + instantiationService = workbenchInstantiationService(); const environmentService = new SettingsTestEnvironmentService(parseArgs(process.argv), process.execPath, path.join(parentDir, 'settings.json')); - const workspaceService = new WorkspaceService(environmentService); + const remoteAgentService = instantiationService.createInstance(RemoteAgentService, {}); + instantiationService.stub(IRemoteAgentService, remoteAgentService); + const workspaceService = new WorkspaceService({}, environmentService, remoteAgentService); - instantiationService = workbenchInstantiationService(); instantiationService.stub(IWorkspaceContextService, workspaceService); instantiationService.stub(IConfigurationService, workspaceService); instantiationService.stub(IEnvironmentService, environmentService); @@ -473,7 +481,9 @@ suite('WorkspaceService - Initialization', () => { const instantiationService = workbenchInstantiationService(); const environmentService = new SettingsTestEnvironmentService(parseArgs(process.argv), process.execPath, globalSettingsFile); - const workspaceService = new WorkspaceService(environmentService); + const remoteAgentService = instantiationService.createInstance(RemoteAgentService, {}); + instantiationService.stub(IRemoteAgentService, remoteAgentService); + const workspaceService = new WorkspaceService({}, environmentService, remoteAgentService); instantiationService.stub(IWorkspaceContextService, workspaceService); instantiationService.stub(IConfigurationService, workspaceService); instantiationService.stub(IEnvironmentService, environmentService); @@ -728,7 +738,9 @@ suite('WorkspaceConfigurationService - Folder', () => { const instantiationService = workbenchInstantiationService(); const environmentService = new SettingsTestEnvironmentService(parseArgs(process.argv), process.execPath, globalSettingsFile); - const workspaceService = new WorkspaceService(environmentService); + const remoteAgentService = instantiationService.createInstance(RemoteAgentService, {}); + instantiationService.stub(IRemoteAgentService, remoteAgentService); + const workspaceService = new WorkspaceService({}, environmentService, remoteAgentService); instantiationService.stub(IWorkspaceContextService, workspaceService); instantiationService.stub(IConfigurationService, workspaceService); instantiationService.stub(IEnvironmentService, environmentService); @@ -1014,10 +1026,12 @@ suite('WorkspaceConfigurationService-Multiroot', () => { parentResource = parentDir; + const instantiationService = workbenchInstantiationService(); environmentService = new SettingsTestEnvironmentService(parseArgs(process.argv), process.execPath, path.join(parentDir, 'settings.json')); - const workspaceService = new WorkspaceService(environmentService); + const remoteAgentService = instantiationService.createInstance(RemoteAgentService, {}); + instantiationService.stub(IRemoteAgentService, remoteAgentService); + const workspaceService = new WorkspaceService({}, environmentService, remoteAgentService); - const instantiationService = workbenchInstantiationService(); instantiationService.stub(IWorkspaceContextService, workspaceService); instantiationService.stub(IConfigurationService, workspaceService); instantiationService.stub(IEnvironmentService, environmentService); diff --git a/src/vs/workbench/services/preferences/browser/preferencesService.ts b/src/vs/workbench/services/preferences/browser/preferencesService.ts index 7879f21bc17..dcb74110f85 100644 --- a/src/vs/workbench/services/preferences/browser/preferencesService.ts +++ b/src/vs/workbench/services/preferences/browser/preferencesService.ts @@ -36,6 +36,7 @@ import { DEFAULT_SETTINGS_EDITOR_SETTING, FOLDER_SETTINGS_PATH, getSettingsTarge import { DefaultPreferencesEditorInput, KeybindingsEditorInput, PreferencesEditorInput, SettingsEditor2Input } from 'vs/workbench/services/preferences/common/preferencesEditorInput'; import { defaultKeybindingsContents, DefaultKeybindingsEditorModel, DefaultSettings, DefaultSettingsEditorModel, Settings2EditorModel, SettingsEditorModel, WorkspaceConfigurationEditorModel, DefaultRawSettingsEditorModel } from 'vs/workbench/services/preferences/common/preferencesModels'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; const emptyEditableSettingsContent = '{\n}'; @@ -69,7 +70,8 @@ export class PreferencesService extends Disposable implements IPreferencesServic @IModelService private readonly modelService: IModelService, @IJSONEditingService private readonly jsonEditingService: IJSONEditingService, @IModeService private readonly modeService: IModeService, - @ILabelService private readonly labelService: ILabelService + @ILabelService private readonly labelService: ILabelService, + @IRemoteAgentService private readonly remoteAgentService: IRemoteAgentService ) { super(); // The default keybindings.json updates based on keyboard layouts, so here we make sure @@ -210,6 +212,15 @@ export class PreferencesService extends Disposable implements IPreferencesServic this.openOrSwitchSettings2(ConfigurationTarget.USER, undefined, options, group); } + async openRemoteSettings(): Promise { + const environemnt = await this.remoteAgentService.getEnvironment(); + if (environemnt) { + await this.createIfNotExists(environemnt.appSettingsPath, emptyEditableSettingsContent); + return this.editorService.openEditor({ resource: environemnt.appSettingsPath, options: { pinned: true, revealIfOpened: true } }); + } + return null; + } + openWorkspaceSettings(jsonEditor?: boolean, options?: ISettingsEditorOptions, group?: IEditorGroup): Promise { jsonEditor = typeof jsonEditor === 'undefined' ? this.configurationService.getValue('workbench.settings.editor') === 'json' : diff --git a/src/vs/workbench/services/preferences/common/preferences.ts b/src/vs/workbench/services/preferences/common/preferences.ts index c5f7f6bd07f..7385ab31222 100644 --- a/src/vs/workbench/services/preferences/common/preferences.ts +++ b/src/vs/workbench/services/preferences/common/preferences.ts @@ -201,6 +201,7 @@ export interface IPreferencesService { openRawDefaultSettings(): Promise; openSettings(jsonEditor?: boolean): Promise; openGlobalSettings(jsonEditor?: boolean, options?: ISettingsEditorOptions, group?: IEditorGroup): Promise; + openRemoteSettings(): Promise; openWorkspaceSettings(jsonEditor?: boolean, options?: ISettingsEditorOptions, group?: IEditorGroup): Promise; openFolderSettings(folder: URI, jsonEditor?: boolean, options?: ISettingsEditorOptions, group?: IEditorGroup): Promise; switchSettings(target: ConfigurationTarget, resource: URI, jsonEditor?: boolean): Promise; -- GitLab