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

Implement #46750

- Introduce Application scope
- Update configuration service and models to respect the application scope
- Update settings editor to respect the application scope
上级 a396179e
......@@ -11,7 +11,6 @@ import { Registry } from 'vs/platform/registry/common/platform';
import * as types from 'vs/base/common/types';
import * as strings from 'vs/base/common/strings';
import { IJSONContributionRegistry, Extensions as JSONExtensions } from 'vs/platform/jsonschemas/common/jsonContributionRegistry';
import { deepClone } from 'vs/base/common/objects';
export const Extensions = {
Configuration: 'base.contributions.configuration'
......@@ -63,8 +62,9 @@ export interface IConfigurationRegistry {
}
export enum ConfigurationScope {
WINDOW = 1,
RESOURCE
APPLICATION = 1,
WINDOW,
RESOURCE,
}
export interface IConfigurationPropertySchema extends IJSONSchema {
......@@ -93,8 +93,10 @@ export interface IDefaultConfigurationExtension {
defaults: { [key: string]: {} };
}
export const settingsSchema: IJSONSchema = { properties: {}, patternProperties: {}, additionalProperties: false, errorMessage: 'Unknown configuration setting' };
export const resourceSettingsSchema: IJSONSchema = { properties: {}, patternProperties: {}, additionalProperties: false, errorMessage: 'Unknown configuration setting' };
export const allSettings: { properties: {}, patternProperties: {} } = { properties: {}, patternProperties: {} };
export const applicationSettings: { properties: {}, patternProperties: {} } = { properties: {}, patternProperties: {} };
export const windowSettings: { properties: {}, patternProperties: {} } = { properties: {}, patternProperties: {} };
export const resourceSettings: { properties: {}, patternProperties: {} } = { properties: {}, patternProperties: {} };
export const editorConfigurationSchemaId = 'vscode://schemas/settings/editor';
const contributionRegistry = Registry.as<IJSONContributionRegistry>(JSONExtensions.JSONContribution);
......@@ -239,10 +241,17 @@ class ConfigurationRegistry implements IConfigurationRegistry {
let properties = configuration.properties;
if (properties) {
for (let key in properties) {
settingsSchema.properties[key] = properties[key];
resourceSettingsSchema.properties[key] = deepClone(properties[key]);
if (properties[key].scope !== ConfigurationScope.RESOURCE) {
resourceSettingsSchema.properties[key].doNotSuggest = true;
allSettings.properties[key] = properties[key];
switch (properties[key].scope) {
case ConfigurationScope.APPLICATION:
applicationSettings.properties[key] = properties[key];
break;
case ConfigurationScope.WINDOW:
windowSettings.properties[key] = properties[key];
break;
case ConfigurationScope.RESOURCE:
resourceSettings.properties[key] = properties[key];
break;
}
}
}
......@@ -262,7 +271,7 @@ class ConfigurationRegistry implements IConfigurationRegistry {
}
private updateOverridePropertyPatternKey(): void {
let patternProperties: IJSONSchema = settingsSchema.patternProperties[this.overridePropertyPattern];
let patternProperties: IJSONSchema = allSettings.patternProperties[this.overridePropertyPattern];
if (!patternProperties) {
patternProperties = {
type: 'object',
......@@ -271,11 +280,18 @@ class ConfigurationRegistry implements IConfigurationRegistry {
$ref: editorConfigurationSchemaId
};
}
delete settingsSchema.patternProperties[this.overridePropertyPattern];
delete allSettings.patternProperties[this.overridePropertyPattern];
delete applicationSettings.patternProperties[this.overridePropertyPattern];
delete windowSettings.patternProperties[this.overridePropertyPattern];
delete resourceSettings.patternProperties[this.overridePropertyPattern];
this.computeOverridePropertyPattern();
settingsSchema.patternProperties[this.overridePropertyPattern] = patternProperties;
resourceSettingsSchema.patternProperties[this.overridePropertyPattern] = patternProperties;
allSettings.patternProperties[this.overridePropertyPattern] = patternProperties;
applicationSettings.patternProperties[this.overridePropertyPattern] = patternProperties;
windowSettings.patternProperties[this.overridePropertyPattern] = patternProperties;
resourceSettings.patternProperties[this.overridePropertyPattern] = patternProperties;
}
private update(configuration: IConfigurationNode): void {
......
......@@ -57,7 +57,6 @@ import { Registry } from 'vs/platform/registry/common/platform';
import { MessageController } from 'vs/editor/contrib/message/messageController';
import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
import { IHashService } from 'vs/workbench/services/hash/common/hashService';
import { ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry';
import { IStringDictionary } from 'vs/base/common/collections';
import { IProgressService } from 'vs/platform/progress/common/progress';
import { ILogService } from 'vs/platform/log/common/log';
......@@ -848,11 +847,23 @@ class SideBySidePreferencesWidget extends Widget {
return TPromise.join([this.updateInput(this.defaultPreferencesEditor, defaultPreferencesEditorInput, DefaultSettingsEditorContribution.ID, editablePreferencesEditorInput.getResource(), options),
this.updateInput(this.editablePreferencesEditor, editablePreferencesEditorInput, SettingsEditorContribution.ID, defaultPreferencesEditorInput.getResource(), options)])
.then(([defaultPreferencesRenderer, editablePreferencesRenderer]) => {
this.defaultPreferencesHeader.textContent = defaultPreferencesRenderer && (<DefaultSettingsEditorModel>defaultPreferencesRenderer.preferencesModel).configurationScope === ConfigurationScope.RESOURCE ? nls.localize('defaultFolderSettings', "Default Folder Settings") : nls.localize('defaultSettings', "Default Settings");
this.defaultPreferencesHeader.textContent = defaultPreferencesRenderer && this.getDefaultPreferencesHeaderText((<DefaultSettingsEditorModel>defaultPreferencesRenderer.preferencesModel).target);
return { defaultPreferencesRenderer, editablePreferencesRenderer };
});
}
private getDefaultPreferencesHeaderText(target: ConfigurationTarget): string {
switch (target) {
case ConfigurationTarget.USER:
return nls.localize('defaultUserSettings', "Default User Settings");
case ConfigurationTarget.WORKSPACE:
return nls.localize('defaultWorkspaceSettings', "Default Workspace Settings");
case ConfigurationTarget.WORKSPACE_FOLDER:
return nls.localize('defaultFolderSettings', "Default Folder Settings");
}
return '';
}
public setResultCount(settingsTarget: SettingsTarget, count: number): void {
this.settingsTargetsWidget.setResultCount(settingsTarget, count);
}
......
......@@ -29,7 +29,6 @@ import { IMarkerService, IMarkerData, MarkerSeverity } from 'vs/platform/markers
import { ICursorPositionChangedEvent } from 'vs/editor/common/controller/cursorEvents';
import { ModelDecorationOptions } from 'vs/editor/common/model/textModel';
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
import { MarkdownString } from 'vs/base/common/htmlContent';
import { overrideIdentifierFromKey, IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { ITextModel, IModelDeltaDecoration, TrackedRangeStickiness } from 'vs/editor/common/model';
......@@ -273,7 +272,7 @@ export class DefaultSettingsRenderer extends Disposable implements IPreferencesR
) {
super();
this.settingHighlighter = this._register(instantiationService.createInstance(SettingHighlighter, editor, this._onFocusPreference, this._onClearFocusPreference));
this.settingsHeaderRenderer = this._register(instantiationService.createInstance(DefaultSettingsHeaderRenderer, editor, preferencesModel.configurationScope));
this.settingsHeaderRenderer = this._register(instantiationService.createInstance(DefaultSettingsHeaderRenderer, editor));
this.settingsGroupTitleRenderer = this._register(instantiationService.createInstance(SettingsGroupTitleRenderer, editor));
this.filteredMatchesRenderer = this._register(instantiationService.createInstance(FilteredMatchesRenderer, editor));
this.editSettingActionRenderer = this._register(instantiationService.createInstance(EditSettingRenderer, editor, preferencesModel, this.settingHighlighter));
......@@ -467,7 +466,7 @@ class DefaultSettingsHeaderRenderer extends Disposable {
private settingsHeaderWidget: DefaultSettingsHeaderWidget;
public readonly onClick: Event<void>;
constructor(editor: ICodeEditor, scope: ConfigurationScope) {
constructor(editor: ICodeEditor) {
super();
this.settingsHeaderWidget = this._register(new DefaultSettingsHeaderWidget(editor, ''));
this.onClick = this.settingsHeaderWidget.onClick;
......@@ -1312,14 +1311,12 @@ class SettingHighlighter extends Disposable {
class UnsupportedSettingsRenderer extends Disposable {
private decorationIds: string[] = [];
private renderingDelayer: Delayer<void> = new Delayer<void>(200);
constructor(
private editor: ICodeEditor,
private settingsEditorModel: SettingsEditorModel,
@IMarkerService private markerService: IMarkerService,
@IEnvironmentService private environmentService: IEnvironmentService
@IMarkerService private markerService: IMarkerService
) {
super();
this._register(this.editor.getModel().onDidChangeContent(() => this.renderingDelayer.trigger(() => this.render())));
......@@ -1327,7 +1324,6 @@ class UnsupportedSettingsRenderer extends Disposable {
public render(): void {
const configurationRegistry = Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration).getConfigurationProperties();
const ranges: IRange[] = [];
const markerData: IMarkerData[] = [];
for (const settingsGroup of this.settingsEditorModel.settingsGroups) {
for (const section of settingsGroup.sections) {
......@@ -1345,17 +1341,6 @@ class UnsupportedSettingsRenderer extends Disposable {
});
}
}
if (this.settingsEditorModel.configurationTarget === ConfigurationTarget.WORKSPACE_FOLDER) {
// Dim and show information for window settings
if (configurationRegistry[setting.key] && configurationRegistry[setting.key].scope === ConfigurationScope.WINDOW) {
ranges.push({
startLineNumber: setting.keyRange.startLineNumber,
startColumn: setting.keyRange.startColumn - 1,
endLineNumber: setting.valueRange.endLineNumber,
endColumn: setting.valueRange.endColumn
});
}
}
}
}
}
......@@ -1364,14 +1349,6 @@ class UnsupportedSettingsRenderer extends Disposable {
} else {
this.markerService.remove('preferencesEditor', [this.settingsEditorModel.uri]);
}
this.decorationIds = this.editor.deltaDecorations(this.decorationIds, ranges.map(range => this.createDecoration(range, this.editor.getModel())));
}
private createDecoration(range: IRange, model: ITextModel): IModelDeltaDecoration {
return {
range,
options: !this.environmentService.isBuilt || this.environmentService.isExtensionDevelopment ? UnsupportedSettingsRenderer._DIM_CONFIGUARATION_DEV_MODE : UnsupportedSettingsRenderer._DIM_CONFIGUARATION_
};
}
private getMarkerMessage(settingKey: string): string {
......@@ -1385,23 +1362,8 @@ class UnsupportedSettingsRenderer extends Disposable {
public dispose(): void {
this.markerService.remove('preferencesEditor', [this.settingsEditorModel.uri]);
this.decorationIds = this.editor.deltaDecorations(this.decorationIds, []);
super.dispose();
}
private static readonly _DIM_CONFIGUARATION_ = ModelDecorationOptions.register({
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
inlineClassName: 'dim-configuration',
beforeContentClassName: 'unsupportedWorkbenhSettingInfo',
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 readonly _DIM_CONFIGUARATION_DEV_MODE = ModelDecorationOptions.register({
stickiness: 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 define it's scope as 'resource' while registering, or when you open this folder directly."))
});
}
class WorkspaceConfigurationRenderer extends Disposable {
......
......@@ -34,7 +34,6 @@ import { Position, IPosition } from 'vs/editor/common/core/position';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IModelService } from 'vs/editor/common/services/modelService';
import { IJSONEditingService } from 'vs/workbench/services/configuration/common/jsonEditing';
import { ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry';
import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
import { IModeService } from 'vs/editor/common/services/modeService';
import { parse } from 'vs/base/common/json';
......@@ -51,10 +50,12 @@ export class PreferencesService extends Disposable implements IPreferencesServic
private readonly _onDispose: Emitter<void> = new Emitter<void>();
private _defaultSettingsUriCounter = 0;
private _defaultSettingsContentModel: DefaultSettings;
private _defaultResourceSettingsUriCounter = 0;
private _defaultResourceSettingsContentModel: DefaultSettings;
private _defaultUserSettingsUriCounter = 0;
private _defaultUserSettingsContentModel: DefaultSettings;
private _defaultWorkspaceSettingsUriCounter = 0;
private _defaultWorkspaceSettingsContentModel: DefaultSettings;
private _defaultFolderSettingsUriCounter = 0;
private _defaultFolderSettingsContentModel: DefaultSettings;
constructor(
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
......@@ -108,9 +109,9 @@ export class PreferencesService extends Disposable implements IPreferencesServic
}
resolveModel(uri: URI): TPromise<ITextModel> {
if (this.isDefaultSettingsResource(uri) || this.isDefaultResourceSettingsResource(uri)) {
if (this.isDefaultSettingsResource(uri)) {
const scope = this.isDefaultSettingsResource(uri) ? ConfigurationScope.WINDOW : ConfigurationScope.RESOURCE;
const target = this.getConfigurationTargetFromDefaultSettingsResource(uri);
const mode = this.modeService.getOrCreateMode('jsonc');
const model = this._register(this.modelService.createModel('', mode, uri));
......@@ -122,7 +123,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic
// model has not been given out => nothing to do
return;
}
defaultSettings = this.getDefaultSettings(scope);
defaultSettings = this.getDefaultSettings(target);
this.modelService.updateModel(model, defaultSettings.parse());
defaultSettings._onDidChange.fire();
}
......@@ -130,7 +131,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic
// Check if Default settings is already created and updated in above promise
if (!defaultSettings) {
defaultSettings = this.getDefaultSettings(scope);
defaultSettings = this.getDefaultSettings(target);
this.modelService.updateModel(model, defaultSettings.parse());
}
......@@ -138,7 +139,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic
}
if (this.defaultSettingsRawResource.toString() === uri.toString()) {
let defaultSettings: DefaultSettings = this.getDefaultSettings(ConfigurationScope.WINDOW);
let defaultSettings: DefaultSettings = this.getDefaultSettings(ConfigurationTarget.USER);
const mode = this.modeService.getOrCreateMode('jsonc');
const model = this._register(this.modelService.createModel(defaultSettings.raw, mode, uri));
return TPromise.as(model);
......@@ -155,7 +156,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic
}
createPreferencesEditorModel(uri: URI): TPromise<IPreferencesEditorModel<any>> {
if (this.isDefaultSettingsResource(uri) || this.isDefaultResourceSettingsResource(uri)) {
if (this.isDefaultSettingsResource(uri)) {
return this.createDefaultSettingsEditorModel(uri);
}
......@@ -271,20 +272,34 @@ export class PreferencesService extends Disposable implements IPreferencesServic
});
}
private getConfigurationTargetFromDefaultSettingsResource(uri: URI) {
return this.isDefaultWorkspaceSettingsResource(uri) ? ConfigurationTarget.WORKSPACE : this.isDefaultFolderSettingsResource(uri) ? ConfigurationTarget.WORKSPACE_FOLDER : ConfigurationTarget.USER;
}
private isDefaultSettingsResource(uri: URI): boolean {
return this.isDefaultUserSettingsResource(uri) || this.isDefaultWorkspaceSettingsResource(uri) || this.isDefaultFolderSettingsResource(uri);
}
private isDefaultUserSettingsResource(uri: URI): boolean {
return uri.authority === 'defaultsettings' && uri.scheme === network.Schemas.vscode && !!uri.path.match(/\/(\d+\/)?settings\.json$/);
}
private isDefaultResourceSettingsResource(uri: URI): boolean {
private isDefaultWorkspaceSettingsResource(uri: URI): boolean {
return uri.authority === 'defaultsettings' && uri.scheme === network.Schemas.vscode && !!uri.path.match(/\/(\d+\/)?workspaceSettings\.json$/);
}
private isDefaultFolderSettingsResource(uri: URI): boolean {
return uri.authority === 'defaultsettings' && uri.scheme === network.Schemas.vscode && !!uri.path.match(/\/(\d+\/)?resourceSettings\.json$/);
}
private getDefaultSettingsResource(configurationTarget: ConfigurationTarget): URI {
if (configurationTarget === ConfigurationTarget.WORKSPACE_FOLDER) {
return URI.from({ scheme: network.Schemas.vscode, authority: 'defaultsettings', path: `/${this._defaultResourceSettingsUriCounter++}/resourceSettings.json` });
} else {
return URI.from({ scheme: network.Schemas.vscode, authority: 'defaultsettings', path: `/${this._defaultSettingsUriCounter++}/settings.json` });
switch (configurationTarget) {
case ConfigurationTarget.WORKSPACE:
return URI.from({ scheme: network.Schemas.vscode, authority: 'defaultsettings', path: `/${this._defaultWorkspaceSettingsUriCounter++}/workspaceSettings.json` });
case ConfigurationTarget.WORKSPACE_FOLDER:
return URI.from({ scheme: network.Schemas.vscode, authority: 'defaultsettings', path: `/${this._defaultFolderSettingsUriCounter++}/resourceSettings.json` });
}
return URI.from({ scheme: network.Schemas.vscode, authority: 'defaultsettings', path: `/${this._defaultUserSettingsUriCounter++}/settings.json` });
}
private getPreferencesEditorInputName(target: ConfigurationTarget, resource: URI): string {
......@@ -314,24 +329,28 @@ export class PreferencesService extends Disposable implements IPreferencesServic
private createDefaultSettingsEditorModel(defaultSettingsUri: URI): TPromise<DefaultSettingsEditorModel> {
return this.textModelResolverService.createModelReference(defaultSettingsUri)
.then(reference => {
const scope = this.isDefaultSettingsResource(defaultSettingsUri) ? ConfigurationScope.WINDOW : ConfigurationScope.RESOURCE;
return this.instantiationService.createInstance(DefaultSettingsEditorModel, defaultSettingsUri, reference, scope, this.getDefaultSettings(scope));
const target = this.getConfigurationTargetFromDefaultSettingsResource(defaultSettingsUri);
return this.instantiationService.createInstance(DefaultSettingsEditorModel, defaultSettingsUri, reference, this.getDefaultSettings(target));
});
}
private getDefaultSettings(scope: ConfigurationScope): DefaultSettings {
switch (scope) {
case ConfigurationScope.WINDOW:
if (!this._defaultSettingsContentModel) {
this._defaultSettingsContentModel = new DefaultSettings(this.getMostCommonlyUsedSettings(), scope);
}
return this._defaultSettingsContentModel;
case ConfigurationScope.RESOURCE:
if (!this._defaultResourceSettingsContentModel) {
this._defaultResourceSettingsContentModel = new DefaultSettings(this.getMostCommonlyUsedSettings(), scope);
}
return this._defaultResourceSettingsContentModel;
private getDefaultSettings(target: ConfigurationTarget): DefaultSettings {
if (target === ConfigurationTarget.WORKSPACE) {
if (!this._defaultWorkspaceSettingsContentModel) {
this._defaultWorkspaceSettingsContentModel = new DefaultSettings(this.getMostCommonlyUsedSettings(), target);
}
return this._defaultWorkspaceSettingsContentModel;
}
if (target === ConfigurationTarget.WORKSPACE_FOLDER) {
if (!this._defaultFolderSettingsContentModel) {
this._defaultFolderSettingsContentModel = new DefaultSettings(this.getMostCommonlyUsedSettings(), target);
}
return this._defaultFolderSettingsContentModel;
}
if (!this._defaultUserSettingsContentModel) {
this._defaultUserSettingsContentModel = new DefaultSettings(this.getMostCommonlyUsedSettings(), target);
}
return this._defaultUserSettingsContentModel;
}
private getEditableSettingsURI(configurationTarget: ConfigurationTarget, resource?: URI): URI {
......
......@@ -403,7 +403,7 @@ export class DefaultSettings extends Disposable {
constructor(
private _mostCommonlyUsedSettingsKeys: string[],
readonly configurationScope: ConfigurationScope,
readonly target: ConfigurationTarget,
) {
super();
}
......@@ -555,10 +555,13 @@ export class DefaultSettings extends Disposable {
}
private matchesScope(property: IConfigurationNode): boolean {
if (this.configurationScope === ConfigurationScope.WINDOW) {
return true;
if (this.target === ConfigurationTarget.WORKSPACE_FOLDER) {
return property.scope === ConfigurationScope.RESOURCE;
}
return property.scope === this.configurationScope;
if (this.target === ConfigurationTarget.WORKSPACE) {
return property.scope === ConfigurationScope.WINDOW || property.scope === ConfigurationScope.RESOURCE;
}
return true;
}
private compareConfigurationNodes(c1: IConfigurationNode, c2: IConfigurationNode): number {
......@@ -603,7 +606,6 @@ export class DefaultSettingsEditorModel extends AbstractSettingsModel implements
constructor(
private _uri: URI,
reference: IReference<ITextEditorModel>,
readonly configurationScope: ConfigurationScope,
private readonly defaultSettings: DefaultSettings
) {
super();
......@@ -617,6 +619,10 @@ export class DefaultSettingsEditorModel extends AbstractSettingsModel implements
return this._uri;
}
public get target(): ConfigurationTarget {
return this.defaultSettings.target;
}
public get settingsGroups(): ISettingsGroup[] {
return this.defaultSettings.settingsGroups;
}
......
......@@ -37,7 +37,7 @@ export class WorkspaceConfigurationModelParser extends ConfigurationModelParser
constructor(name: string) {
super(name);
this._settingsModelParser = new FolderSettingsModelParser(name);
this._settingsModelParser = new FolderSettingsModelParser(name, [ConfigurationScope.WINDOW, ConfigurationScope.RESOURCE]);
this._launchModel = new ConfigurationModel();
}
......@@ -98,7 +98,7 @@ export class FolderSettingsModelParser extends ConfigurationModelParser {
private _raw: any;
private _settingsModel: SettingsModel;
constructor(name: string, private configurationScope?: ConfigurationScope) {
constructor(name: string, private scopes: ConfigurationScope[]) {
super(name);
}
......@@ -125,7 +125,8 @@ export class FolderSettingsModelParser extends ConfigurationModelParser {
const configurationProperties = Registry.as<IConfigurationRegistry>(Extensions.Configuration).getConfigurationProperties();
for (let key in rawSettings) {
if (this.isNotExecutable(key, configurationProperties)) {
if (this.configurationScope === void 0 || this.getScope(key, configurationProperties) === this.configurationScope) {
const scope = this.getScope(key, configurationProperties);
if (this.scopes.indexOf(scope) !== -1) {
rawWorkspaceSettings[key] = rawSettings[key];
}
} else {
......
......@@ -187,7 +187,7 @@ export class FolderConfiguration extends Disposable {
constructor(private folder: URI, private configFolderRelativePath: string, workbenchState: WorkbenchState) {
super();
this._folderSettingsModelParser = new FolderSettingsModelParser(FOLDER_SETTINGS_PATH, WorkbenchState.WORKSPACE === workbenchState ? ConfigurationScope.RESOURCE : void 0);
this._folderSettingsModelParser = new FolderSettingsModelParser(FOLDER_SETTINGS_PATH, WorkbenchState.WORKSPACE === workbenchState ? [ConfigurationScope.RESOURCE] : [ConfigurationScope.WINDOW, ConfigurationScope.RESOURCE]);
this.workspaceFilePathToConfiguration = Object.create(null);
this.reloadConfigurationScheduler = this._register(new RunOnceScheduler(() => this.loadConfiguration().then(configuration => this.reloadConfigurationEventEmitter.fire(configuration), errors.onUnexpectedError), FolderConfiguration.RELOAD_CONFIGURATION_DELAY));
}
......
......@@ -39,6 +39,11 @@ export enum ConfigurationEditingErrorCode {
*/
ERROR_UNKNOWN_KEY,
/**
* Error when trying to write an application setting into workspace settings.
*/
ERROR_INVALID_WORKSPACE_CONFIGURATION_APPLICATION,
/**
* Error when trying to write an invalid folder configuration key to folder settings.
*/
......@@ -274,6 +279,7 @@ export class ConfigurationEditingService {
// API constraints
case ConfigurationEditingErrorCode.ERROR_UNKNOWN_KEY: return nls.localize('errorUnknownKey', "Unable to write to {0} because {1} is not a registered configuration.", this.stringifyTarget(target), operation.key);
case ConfigurationEditingErrorCode.ERROR_INVALID_WORKSPACE_CONFIGURATION_APPLICATION: return nls.localize('errorInvalidWorkspaceConfigurationApplication', "Unable to write {0} to Workspace Settings. This setting can be written only into User settings.", operation.key);
case ConfigurationEditingErrorCode.ERROR_INVALID_FOLDER_CONFIGURATION: return nls.localize('errorInvalidFolderConfiguration', "Unable to write to Folder Settings because {0} does not support the folder resource scope.", operation.key);
case ConfigurationEditingErrorCode.ERROR_INVALID_USER_TARGET: return nls.localize('errorInvalidUserTarget', "Unable to write to User Settings because {0} does not support for global scope.", operation.key);
case ConfigurationEditingErrorCode.ERROR_INVALID_WORKSPACE_TARGET: return nls.localize('errorInvalidWorkspaceTarget', "Unable to write to Workspace Settings because {0} does not support for workspace scope in a multi folder workspace.", operation.key);
......@@ -396,6 +402,15 @@ export class ConfigurationEditingService {
return this.wrapError(ConfigurationEditingErrorCode.ERROR_NO_WORKSPACE_OPENED, target, operation);
}
if (target === ConfigurationTarget.WORKSPACE) {
if (!operation.workspaceStandAloneConfigurationKey) {
const configurationProperties = Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration).getConfigurationProperties();
if (configurationProperties[operation.key].scope === ConfigurationScope.APPLICATION) {
return this.wrapError(ConfigurationEditingErrorCode.ERROR_INVALID_WORKSPACE_CONFIGURATION_APPLICATION, target, operation);
}
}
}
if (target === ConfigurationTarget.WORKSPACE_FOLDER) {
if (!operation.resource) {
return this.wrapError(ConfigurationEditingErrorCode.ERROR_INVALID_FOLDER_TARGET, target, operation);
......
......@@ -10,7 +10,7 @@ import { dirname, basename } from 'path';
import * as assert from 'vs/base/common/assert';
import { Event, Emitter } from 'vs/base/common/event';
import { StrictResourceMap } from 'vs/base/common/map';
import { equals } from 'vs/base/common/objects';
import { equals, deepClone } from 'vs/base/common/objects';
import { Disposable } from 'vs/base/common/lifecycle';
import { Queue } from 'vs/base/common/async';
import { stat, writeFile } from 'vs/base/node/pfs';
......@@ -24,7 +24,7 @@ import { IConfigurationChangeEvent, ConfigurationTarget, IConfigurationOverrides
import { Configuration, WorkspaceConfigurationChangeEvent, AllKeysConfigurationChangeEvent } from 'vs/workbench/services/configuration/common/configurationModels';
import { IWorkspaceConfigurationService, FOLDER_CONFIG_FOLDER_NAME, defaultSettingsSchemaId, userSettingsSchemaId, workspaceSettingsSchemaId, folderSettingsSchemaId } from 'vs/workbench/services/configuration/common/configuration';
import { Registry } from 'vs/platform/registry/common/platform';
import { IConfigurationNode, IConfigurationRegistry, Extensions, settingsSchema, resourceSettingsSchema, IConfigurationPropertySchema } from 'vs/platform/configuration/common/configurationRegistry';
import { IConfigurationNode, IConfigurationRegistry, Extensions, IConfigurationPropertySchema, allSettings, windowSettings, resourceSettings, applicationSettings } from 'vs/platform/configuration/common/configurationRegistry';
import { createHash } from 'crypto';
import { getWorkspaceLabel, IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier, IStoredWorkspaceFolder, isStoredWorkspaceFolder, IWorkspaceFolderCreationData } from 'vs/platform/workspaces/common/workspaces';
import { IWindowConfiguration } from 'vs/platform/windows/common/windows';
......@@ -40,6 +40,8 @@ import { massageFolderPathForWorkspace } from 'vs/platform/workspaces/node/works
import { distinct } from 'vs/base/common/arrays';
import { UserConfiguration } from 'vs/platform/configuration/node/configuration';
import { getBaseLabel } from 'vs/base/common/labels';
import { IJSONSchema, IJSONSchemaMap } from 'vs/base/common/jsonSchema';
import { localize } from 'vs/nls';
export class WorkspaceService extends Disposable implements IWorkspaceConfigurationService, IWorkspaceContextService {
......@@ -489,15 +491,29 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat
private registerConfigurationSchemas(): void {
if (this.workspace) {
const jsonRegistry = Registry.as<IJSONContributionRegistry>(JSONExtensions.JSONContribution);
jsonRegistry.registerSchema(defaultSettingsSchemaId, settingsSchema);
jsonRegistry.registerSchema(userSettingsSchemaId, settingsSchema);
const convertToNotSuggestedProperties = (properties: IJSONSchemaMap, errorMessage: string): IJSONSchemaMap => {
return Object.keys(properties).reduce((result: IJSONSchemaMap, property) => {
result[property] = deepClone(properties[property]);
result[property].deprecationMessage = errorMessage;
return result;
}, {});
};
const allSettingsSchema: IJSONSchema = { properties: allSettings.properties, patternProperties: allSettings.patternProperties, additionalProperties: false, errorMessage: 'Unknown configuration setting' };
const unsupportedApplicationSettings = convertToNotSuggestedProperties(applicationSettings.properties, localize('unsupportedApplicationSetting', "This setting can be applied only in User Settings"));
const workspaceSettingsSchema: IJSONSchema = { properties: { ...unsupportedApplicationSettings, ...windowSettings.properties, ...resourceSettings.properties }, patternProperties: allSettings.patternProperties, additionalProperties: false, errorMessage: 'Unknown configuration setting' };
jsonRegistry.registerSchema(defaultSettingsSchemaId, allSettingsSchema);
jsonRegistry.registerSchema(userSettingsSchemaId, allSettingsSchema);
if (WorkbenchState.WORKSPACE === this.getWorkbenchState()) {
jsonRegistry.registerSchema(workspaceSettingsSchemaId, settingsSchema);
jsonRegistry.registerSchema(folderSettingsSchemaId, resourceSettingsSchema);
const unsupportedWindowSettings = convertToNotSuggestedProperties(windowSettings.properties, localize('unsupportedWindowSetting', "This setting cannot be applied now. It will be applied when you open this folder directly."));
const folderSettingsSchema: IJSONSchema = { properties: { ...unsupportedApplicationSettings, ...unsupportedWindowSettings, ...resourceSettings.properties }, patternProperties: allSettings.patternProperties, additionalProperties: false, errorMessage: 'Unknown configuration setting' };
jsonRegistry.registerSchema(workspaceSettingsSchemaId, workspaceSettingsSchema);
jsonRegistry.registerSchema(folderSettingsSchemaId, folderSettingsSchema);
} else {
jsonRegistry.registerSchema(workspaceSettingsSchemaId, settingsSchema);
jsonRegistry.registerSchema(folderSettingsSchemaId, settingsSchema);
jsonRegistry.registerSchema(workspaceSettingsSchemaId, workspaceSettingsSchema);
jsonRegistry.registerSchema(folderSettingsSchemaId, workspaceSettingsSchema);
}
}
}
......
......@@ -42,7 +42,7 @@ suite('FolderSettingsModelParser', () => {
});
test('parse all folder settings', () => {
const testObject = new FolderSettingsModelParser('settings');
const testObject = new FolderSettingsModelParser('settings', [ConfigurationScope.RESOURCE, ConfigurationScope.WINDOW]);
testObject.parse(JSON.stringify({ 'FolderSettingsModelParser.window': 'window', 'FolderSettingsModelParser.resource': 'resource', 'FolderSettingsModelParser.executable': 'executable' }));
......@@ -50,7 +50,7 @@ suite('FolderSettingsModelParser', () => {
});
test('parse resource folder settings', () => {
const testObject = new FolderSettingsModelParser('settings', ConfigurationScope.RESOURCE);
const testObject = new FolderSettingsModelParser('settings', [ConfigurationScope.RESOURCE]);
testObject.parse(JSON.stringify({ 'FolderSettingsModelParser.window': 'window', 'FolderSettingsModelParser.resource': 'resource', 'FolderSettingsModelParser.executable': 'executable' }));
......@@ -58,7 +58,7 @@ suite('FolderSettingsModelParser', () => {
});
test('reprocess folder settings excludes executable', () => {
const testObject = new FolderSettingsModelParser('settings');
const testObject = new FolderSettingsModelParser('settings', [ConfigurationScope.RESOURCE, ConfigurationScope.WINDOW]);
testObject.parse(JSON.stringify({ 'FolderSettingsModelParser.resource': 'resource', 'FolderSettingsModelParser.anotherExecutable': 'executable' }));
......
......@@ -632,6 +632,11 @@ suite('WorkspaceConfigurationService - Folder', () => {
'id': '_test',
'type': 'object',
'properties': {
'configurationService.folder.applicationSetting': {
'type': 'string',
'default': 'isSet',
scope: ConfigurationScope.APPLICATION
},
'configurationService.folder.testSetting': {
'type': 'string',
'default': 'isSet',
......@@ -682,7 +687,7 @@ suite('WorkspaceConfigurationService - Folder', () => {
});
test('defaults', () => {
assert.deepEqual(testObject.getValue('configurationService'), { 'folder': { 'testSetting': 'isSet', 'executableSetting': 'isSet' } });
assert.deepEqual(testObject.getValue('configurationService'), { 'folder': { 'applicationSetting': 'isSet', 'testSetting': 'isSet', 'executableSetting': 'isSet' } });
});
test('globals override defaults', () => {
......@@ -731,6 +736,13 @@ suite('WorkspaceConfigurationService - Folder', () => {
});
});
test('application settings are not read from workspace', () => {
fs.writeFileSync(globalSettingsFile, '{ "configurationService.folder.applicationSetting": "userValue" }');
fs.writeFileSync(path.join(workspaceDir, '.vscode', 'settings.json'), '{ "configurationService.folder.applicationSetting": "workspaceValue" }');
return testObject.reloadConfiguration()
.then(() => assert.equal(testObject.getValue('configurationService.folder.applicationSetting'), 'userValue'));
});
test('executable settings are not read from workspace', () => {
fs.writeFileSync(globalSettingsFile, '{ "configurationService.folder.executableSetting": "userValue" }');
fs.writeFileSync(path.join(workspaceDir, '.vscode', 'settings.json'), '{ "configurationService.folder.executableSetting": "workspaceValue" }');
......@@ -880,6 +892,11 @@ suite('WorkspaceConfigurationService - Folder', () => {
.then(() => assert.equal(testObject.getValue('tasks.service.testSetting'), 'value'));
});
test('update application setting into workspace configuration in a workspace is not supported', () => {
return testObject.updateValue('configurationService.folder.applicationSetting', 'workspaceValue', {}, ConfigurationTarget.WORKSPACE, true)
.then(() => assert.fail('Should not be supported'), (e) => assert.equal(e.code, ConfigurationEditingErrorCode.ERROR_INVALID_WORKSPACE_CONFIGURATION_APPLICATION));
});
test('update tasks configuration', () => {
return testObject.updateValue('tasks', { 'version': '1.0.0', tasks: [{ 'taskName': 'myTask' }] }, ConfigurationTarget.WORKSPACE)
.then(() => assert.deepEqual(testObject.getValue('tasks'), { 'version': '1.0.0', tasks: [{ 'taskName': 'myTask' }] }));
......@@ -922,6 +939,11 @@ suite('WorkspaceConfigurationService - Multiroot', () => {
'type': 'string',
'default': 'isSet'
},
'configurationService.workspace.applicationSetting': {
'type': 'string',
'default': 'isSet',
scope: ConfigurationScope.APPLICATION
},
'configurationService.workspace.testResourceSetting': {
'type': 'string',
'default': 'isSet',
......@@ -1001,6 +1023,13 @@ suite('WorkspaceConfigurationService - Multiroot', () => {
.then(() => assert.deepEqual(testObject.getUnsupportedWorkspaceKeys(), ['configurationService.workspace.testExecutableSetting', 'configurationService.workspace.testExecutableResourceSetting']));
});
test('application settings are not read from workspace', () => {
fs.writeFileSync(environmentService.appSettingsPath, '{ "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('workspace settings override user settings after defaults are registered ', () => {
fs.writeFileSync(environmentService.appSettingsPath, '{ "configurationService.workspace.newSetting": "userValue" }');
return jsonEditingServce.write(workspaceContextService.getWorkspace().configuration, { key: 'settings', value: { 'configurationService.workspace.newSetting': 'workspaceValue' } }, true)
......@@ -1020,6 +1049,13 @@ suite('WorkspaceConfigurationService - Multiroot', () => {
});
});
test('application settings are not read from workspace folder', () => {
fs.writeFileSync(environmentService.appSettingsPath, '{ "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('executable settings are not read from workspace folder after defaults are registered', () => {
fs.writeFileSync(environmentService.appSettingsPath, '{ "configurationService.workspace.testNewExecutableResourceSetting": "userValue" }');
fs.writeFileSync(workspaceContextService.getWorkspace().folders[0].toResource('.vscode/settings.json').fsPath, '{ "configurationService.workspace.testNewExecutableResourceSetting": "workspaceFolderValue" }');
......@@ -1206,6 +1242,11 @@ suite('WorkspaceConfigurationService - Multiroot', () => {
.then(() => assert.ok(target.called));
});
test('update application setting into workspace configuration in a workspace is not supported', () => {
return testObject.updateValue('configurationService.workspace.applicationSetting', 'workspaceValue', {}, ConfigurationTarget.WORKSPACE, true)
.then(() => assert.fail('Should not be supported'), (e) => assert.equal(e.code, ConfigurationEditingErrorCode.ERROR_INVALID_WORKSPACE_CONFIGURATION_APPLICATION));
});
test('update workspace folder configuration', () => {
const workspace = workspaceContextService.getWorkspace();
return testObject.updateValue('configurationService.workspace.testResourceSetting', 'workspaceFolderValue', { resource: workspace.folders[0].uri }, ConfigurationTarget.WORKSPACE_FOLDER)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册