From a10370cb9fc66f51279a623e9a45c8d32814b765 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Fri, 27 Oct 2017 12:30:26 +0200 Subject: [PATCH] Implement #36623 --- .../common/configurationRegistry.ts | 5 ++ src/vs/workbench/api/node/extHost.api.impl.ts | 2 +- src/vs/workbench/api/node/extHost.protocol.ts | 7 ++- .../api/node/extHostConfiguration.ts | 50 ++++++++++++++++--- .../browser/preferencesRenderers.ts | 11 +++- .../electron-browser/extensionHost.ts | 7 ++- .../api/extHostConfiguration.test.ts | 16 +++--- 7 files changed, 81 insertions(+), 17 deletions(-) diff --git a/src/vs/platform/configuration/common/configurationRegistry.ts b/src/vs/platform/configuration/common/configurationRegistry.ts index c818c1f454f..14bc0c42cf8 100644 --- a/src/vs/platform/configuration/common/configurationRegistry.ts +++ b/src/vs/platform/configuration/common/configurationRegistry.ts @@ -316,4 +316,9 @@ export function validateProperty(property: string): string { return nls.localize('config.property.duplicate', "Cannot register '{0}'. This property is already registered.", property); } return null; +} + +export function getScopes(keys: string[]): ConfigurationScope[] { + const configurationProperties = configurationRegistry.getConfigurationProperties(); + return keys.map(key => configurationProperties[key].scope); } \ No newline at end of file diff --git a/src/vs/workbench/api/node/extHost.api.impl.ts b/src/vs/workbench/api/node/extHost.api.impl.ts index 1d9c80b498f..3c22ef62f9a 100644 --- a/src/vs/workbench/api/node/extHost.api.impl.ts +++ b/src/vs/workbench/api/node/extHost.api.impl.ts @@ -467,7 +467,7 @@ export function createApiFactory( return extHostConfiguration.onDidChangeConfiguration(listener, thisArgs, disposables); }, getConfiguration: (section?: string, resource?: vscode.Uri): vscode.WorkspaceConfiguration => { - return extHostConfiguration.getConfiguration(section, resource); + return extHostConfiguration.getConfiguration(section, resource, extension.id); }, registerTextDocumentContentProvider(scheme: string, provider: vscode.TextDocumentContentProvider) { return extHostDocumentContentProviders.registerTextDocumentContentProvider(scheme, provider); diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index 62c7bbc1f4e..4101823ca55 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -49,6 +49,7 @@ import { SerializedError } from 'vs/base/common/errors'; import { IRelativePattern } from 'vs/base/common/glob'; import { IWorkspaceFolderData } from 'vs/platform/workspace/common/workspace'; import { IStat, IFileChange } from 'vs/platform/files/common/files'; +import { ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; export interface IEnvironment { isExtensionDevelopmentDebug: boolean; @@ -73,10 +74,14 @@ export interface IInitData { environment: IEnvironment; workspace: IWorkspaceData; extensions: IExtensionDescription[]; - configuration: IConfigurationData; + configuration: IConfigurationInitData; telemetryInfo: ITelemetryInfo; } +export interface IConfigurationInitData extends IConfigurationData { + configurationScopes: ConfigurationScope[]; +} + export interface IWorkspaceConfigurationChangeEventData { changedConfiguration: IConfigurationModel; changedConfigurationByResource: { [folder: string]: IConfigurationModel }; diff --git a/src/vs/workbench/api/node/extHostConfiguration.ts b/src/vs/workbench/api/node/extHostConfiguration.ts index 69a4cc1fdfb..b1f27b35e09 100644 --- a/src/vs/workbench/api/node/extHostConfiguration.ts +++ b/src/vs/workbench/api/node/extHostConfiguration.ts @@ -9,12 +9,13 @@ import URI from 'vs/base/common/uri'; import Event, { Emitter } from 'vs/base/common/event'; import * as vscode from 'vscode'; import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace'; -import { ExtHostConfigurationShape, MainThreadConfigurationShape, IWorkspaceConfigurationChangeEventData } from './extHost.protocol'; +import { ExtHostConfigurationShape, MainThreadConfigurationShape, IWorkspaceConfigurationChangeEventData, IConfigurationInitData } from './extHost.protocol'; import { ConfigurationTarget as ExtHostConfigurationTarget } from './extHostTypes'; import { IConfigurationData, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; import { Configuration, ConfigurationModel, ConfigurationChangeEvent } from 'vs/platform/configuration/common/configurationModels'; import { WorkspaceConfigurationChangeEvent } from 'vs/workbench/services/configuration/common/configurationModels'; import { StrictResourceMap } from 'vs/base/common/map'; +import { ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; function lookUp(tree: any, key: string) { if (key) { @@ -40,12 +41,14 @@ export class ExtHostConfiguration implements ExtHostConfigurationShape { private readonly _onDidChangeConfiguration = new Emitter(); private readonly _proxy: MainThreadConfigurationShape; private readonly _extHostWorkspace: ExtHostWorkspace; + private _configurationScopes: Map; private _configuration: Configuration; - constructor(proxy: MainThreadConfigurationShape, extHostWorkspace: ExtHostWorkspace, data: IConfigurationData) { + constructor(proxy: MainThreadConfigurationShape, extHostWorkspace: ExtHostWorkspace, data: IConfigurationInitData) { this._proxy = proxy; this._extHostWorkspace = extHostWorkspace; this._configuration = Configuration.parse(data); + this._readConfigurationScopes(data.configurationScopes); } get onDidChangeConfiguration(): Event { @@ -54,14 +57,18 @@ export class ExtHostConfiguration implements ExtHostConfigurationShape { $acceptConfigurationChanged(data: IConfigurationData, eventData: IWorkspaceConfigurationChangeEventData) { this._configuration = Configuration.parse(data); - this._onDidChangeConfiguration.fire(this.toConfigurationChangeEvent(eventData)); + this._onDidChangeConfiguration.fire(this._toConfigurationChangeEvent(eventData)); } - getConfiguration(section?: string, resource?: URI): vscode.WorkspaceConfiguration { + getConfiguration(section?: string, resource?: URI, extensionId?: string): vscode.WorkspaceConfiguration { const config = section ? lookUp(this._configuration.getSection(null, { resource }, this._extHostWorkspace.workspace), section) : this._configuration.getSection(null, { resource }, this._extHostWorkspace.workspace); + if (section) { + this._validateConfigurationAccess(section, resource, extensionId); + } + function parseConfigurationTarget(arg: boolean | ExtHostConfigurationTarget): ConfigurationTarget { if (arg === void 0 || arg === null) { return null; @@ -81,7 +88,8 @@ export class ExtHostConfiguration implements ExtHostConfigurationShape { has(key: string): boolean { return typeof lookUp(config, key) !== 'undefined'; }, - get(key: string, defaultValue?: T): T { + get: (key: string, defaultValue?: T) => { + this._validateConfigurationAccess(section ? `${section}.${key}` : key, resource, extensionId); let result = lookUp(config, key); if (typeof result === 'undefined') { result = defaultValue; @@ -90,6 +98,7 @@ export class ExtHostConfiguration implements ExtHostConfigurationShape { }, update: (key: string, value: any, arg: ExtHostConfigurationTarget | boolean) => { key = section ? `${section}.${key}` : key; + this._validateConfigurationAccess(key, resource, extensionId); const target = parseConfigurationTarget(arg); if (value !== void 0) { return this._proxy.$updateConfigurationOption(target, key, value, resource); @@ -120,7 +129,36 @@ export class ExtHostConfiguration implements ExtHostConfigurationShape { return Object.freeze(result); } - private toConfigurationChangeEvent(data: IWorkspaceConfigurationChangeEventData): vscode.ConfigurationChangeEvent { + private _validateConfigurationAccess(key: string, resource: URI, extensionId: string): void { + const scope = this._configurationScopes.get(key); + const extensionIdText = extensionId ? `[${extensionId}] ` : ''; + if (ConfigurationScope.RESOURCE === scope) { + if (!resource) { + console.warn(`${extensionIdText}Accessing a resource scoped configuration without providing a resource is not expected. To get the effective value for '${key}', provide the resource for which the value is needed. If you would like to look up all values, use 'inspect' method instead.`); + } + return; + } + if (ConfigurationScope.WINDOW === scope) { + if (resource) { + console.warn(`${extensionIdText}Accessing a window scoped configuration for a resource is not expected. To associate '${key}' to a resource, define its scope to 'resource' in configuration contributions in 'package.json'.`); + } + return; + } + } + + private _readConfigurationScopes(scopes: ConfigurationScope[]): void { + this._configurationScopes = new Map(); + if (scopes.length) { + const defaultKeys = this._configuration.keys(this._extHostWorkspace.workspace).default; + if (defaultKeys.length === scopes.length) { + for (let i = 0; i < defaultKeys.length; i++) { + this._configurationScopes.set(defaultKeys[i], scopes[i]); + } + } + } + } + + private _toConfigurationChangeEvent(data: IWorkspaceConfigurationChangeEventData): vscode.ConfigurationChangeEvent { const changedConfiguration = new ConfigurationModel(data.changedConfiguration.contents, data.changedConfiguration.keys, data.changedConfiguration.overrides); const changedConfigurationByResource: StrictResourceMap = new StrictResourceMap(); for (const key of Object.keys(data.changedConfigurationByResource)) { diff --git a/src/vs/workbench/parts/preferences/browser/preferencesRenderers.ts b/src/vs/workbench/parts/preferences/browser/preferencesRenderers.ts index 4795b7be614..acb60e3acce 100644 --- a/src/vs/workbench/parts/preferences/browser/preferencesRenderers.ts +++ b/src/vs/workbench/parts/preferences/browser/preferencesRenderers.ts @@ -33,6 +33,7 @@ import { ModelDecorationOptions } from 'vs/editor/common/model/textModelWithDeco import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { MarkdownString } from 'vs/base/common/htmlContent'; import { overrideIdentifierFromKey, IConfigurationService, ConfigurationTarget, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; export interface IPreferencesRenderer extends IDisposable { preferencesModel: IPreferencesEditorModel; @@ -1150,6 +1151,7 @@ class UnsupportedWorkbenchSettingsRenderer extends Disposable { constructor(private editor: editorCommon.ICommonCodeEditor, private workspaceSettingsEditorModel: SettingsEditorModel, @IWorkspaceConfigurationService private configurationService: IWorkspaceConfigurationService, + @IEnvironmentService private environmentService: IEnvironmentService ) { super(); this._register(this.editor.getModel().onDidChangeContent(() => this.renderingDelayer.trigger(() => this.render()))); @@ -1182,10 +1184,17 @@ class UnsupportedWorkbenchSettingsRenderer extends Disposable { hoverMessage: new MarkdownString().appendText(nls.localize('unsupportedWorkbenchSetting', "This setting cannot be applied now. It will be applied when you open this folder directly.")) }); + private static _DIM_CONFIGUARATION_DEV_MODE = ModelDecorationOptions.register({ + stickiness: editorCommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, + inlineClassName: 'dim-configuration', + beforeContentClassName: 'unsupportedWorkbenhSettingInfo', + hoverMessage: new MarkdownString().appendText(nls.localize('unsupportedWorkbenchSettingDevMode', "This setting cannot be applied now. It will be applied if you change its scope to 'resource' scope or when you open this folder directly.")) + }); + private createDecoration(range: IRange, model: editorCommon.IModel): editorCommon.IModelDeltaDecoration { return { range, - options: UnsupportedWorkbenchSettingsRenderer._DIM_CONFIGUARATION_ + options: !this.environmentService.isBuilt || this.environmentService.isExtensionDevelopment ? UnsupportedWorkbenchSettingsRenderer._DIM_CONFIGUARATION_DEV_MODE : UnsupportedWorkbenchSettingsRenderer._DIM_CONFIGUARATION_ }; } diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts b/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts index 126d98d7cc0..d1406d9b496 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts @@ -27,7 +27,7 @@ import { generateRandomPipeName, Protocol } from 'vs/base/parts/ipc/node/ipc.net import { createServer, Server, Socket } from 'net'; import Event, { Emitter, debounceEvent, mapEvent, anyEvent } from 'vs/base/common/event'; import { fromEventEmitter } from 'vs/base/node/event'; -import { IInitData, IWorkspaceData } from 'vs/workbench/api/node/extHost.protocol'; +import { IInitData, IWorkspaceData, IConfigurationInitData } from 'vs/workbench/api/node/extHost.protocol'; import { IExtensionService } from 'vs/platform/extensions/common/extensions'; import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; import { ICrashReporterService } from 'vs/workbench/services/crashReporter/common/crashReporterService'; @@ -36,6 +36,7 @@ import { isEqual } from 'vs/base/common/paths'; import { EXTENSION_CLOSE_EXTHOST_BROADCAST_CHANNEL, EXTENSION_RELOAD_BROADCAST_CHANNEL, EXTENSION_ATTACH_BROADCAST_CHANNEL, EXTENSION_LOG_BROADCAST_CHANNEL, EXTENSION_TERMINATE_BROADCAST_CHANNEL } from 'vs/platform/extensions/common/extensionHost'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { IRemoteConsoleLog, log, parse } from 'vs/base/node/console'; +import { getScopes } from 'vs/platform/configuration/common/configurationRegistry'; export class ExtensionHostProcessWorker { @@ -344,6 +345,7 @@ export class ExtensionHostProcessWorker { private _createExtHostInitData(): TPromise { return TPromise.join([this._telemetryService.getTelemetryInfo(), this._extensionService.getExtensions()]).then(([telemetryInfo, extensionDescriptions]) => { + const configurationData: IConfigurationInitData = { ...this._configurationService.getConfigurationData(), configurationScopes: [] }; const r: IInitData = { parentPid: process.pid, environment: { @@ -360,7 +362,8 @@ export class ExtensionHostProcessWorker { }, workspace: this._contextService.getWorkbenchState() === WorkbenchState.EMPTY ? null : this._contextService.getWorkspace(), extensions: extensionDescriptions, - configuration: this._configurationService.getConfigurationData(), + // Send configurations scopes only in development mode. + configuration: !this._environmentService.isBuilt || this._environmentService.isExtensionDevelopment ? { ...configurationData, configurationScopes: getScopes(this._configurationService.keys().default) } : configurationData, telemetryInfo }; return r; diff --git a/src/vs/workbench/test/electron-browser/api/extHostConfiguration.test.ts b/src/vs/workbench/test/electron-browser/api/extHostConfiguration.test.ts index 1d750b6730c..5014719ddb2 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostConfiguration.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostConfiguration.test.ts @@ -9,7 +9,7 @@ import * as assert from 'assert'; import URI from 'vs/base/common/uri'; import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace'; import { ExtHostConfiguration } from 'vs/workbench/api/node/extHostConfiguration'; -import { MainThreadConfigurationShape } from 'vs/workbench/api/node/extHost.protocol'; +import { MainThreadConfigurationShape, IConfigurationInitData } from 'vs/workbench/api/node/extHost.protocol'; import { TPromise } from 'vs/base/common/winjs.base'; import { ConfigurationModel } from 'vs/platform/configuration/common/configurationModels'; import { TestThreadService } from './testThreadService'; @@ -34,12 +34,13 @@ suite('ExtHostConfiguration', function () { return new ExtHostConfiguration(shape, new ExtHostWorkspace(new TestThreadService(), null), createConfigurationData(contents)); } - function createConfigurationData(contents: any) { + function createConfigurationData(contents: any): IConfigurationInitData { return { defaults: new ConfigurationModel(contents), user: new ConfigurationModel(contents), workspace: new ConfigurationModel(), - folders: Object.create(null) + folders: Object.create(null), + configurationScopes: [] }; } @@ -106,7 +107,8 @@ suite('ExtHostConfiguration', function () { } }, ['editor.wordWrap']), workspace: new ConfigurationModel({}, []), - folders: Object.create(null) + folders: Object.create(null), + configurationScopes: [] } ); @@ -151,7 +153,8 @@ suite('ExtHostConfiguration', function () { } }, ['editor.wordWrap']), workspace, - folders + folders, + configurationScopes: [] } ); @@ -224,7 +227,8 @@ suite('ExtHostConfiguration', function () { } }, ['editor.wordWrap']), workspace, - folders + folders, + configurationScopes: [] } );