mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 10:58:30 -05:00
Merge VS Code 1.31.1 (#4283)
This commit is contained in:
@@ -1,260 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import * as objects from 'vs/base/common/objects';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { IJSONSchema } from 'vs/base/common/jsonSchema';
|
||||
import { ExtensionsRegistry, IExtensionPointUser } from 'vs/workbench/services/extensions/common/extensionsRegistry';
|
||||
import { IConfigurationNode, IConfigurationRegistry, Extensions, editorConfigurationSchemaId, IDefaultConfigurationExtension, validateProperty, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
import { IJSONContributionRegistry, Extensions as JSONExtensions } from 'vs/platform/jsonschemas/common/jsonContributionRegistry';
|
||||
import { workspaceSettingsSchemaId, launchSchemaId } from 'vs/workbench/services/configuration/common/configuration';
|
||||
import { isObject } from 'vs/base/common/types';
|
||||
|
||||
const configurationRegistry = Registry.as<IConfigurationRegistry>(Extensions.Configuration);
|
||||
|
||||
const configurationEntrySchema: IJSONSchema = {
|
||||
type: 'object',
|
||||
defaultSnippets: [{ body: { title: '', properties: {} } }],
|
||||
properties: {
|
||||
title: {
|
||||
description: nls.localize('vscode.extension.contributes.configuration.title', 'A summary of the settings. This label will be used in the settings file as separating comment.'),
|
||||
type: 'string'
|
||||
},
|
||||
properties: {
|
||||
description: nls.localize('vscode.extension.contributes.configuration.properties', 'Description of the configuration properties.'),
|
||||
type: 'object',
|
||||
additionalProperties: {
|
||||
anyOf: [
|
||||
{ $ref: 'http://json-schema.org/draft-07/schema#' },
|
||||
{
|
||||
type: 'object',
|
||||
properties: {
|
||||
isExecutable: {
|
||||
type: 'boolean',
|
||||
deprecationMessage: 'This property is deprecated. Instead use `scope` property and set it to `application` value.'
|
||||
},
|
||||
scope: {
|
||||
type: 'string',
|
||||
enum: ['application', 'window', 'resource'],
|
||||
default: 'window',
|
||||
enumDescriptions: [
|
||||
nls.localize('scope.application.description', "Application specific configuration, which can be configured only in User settings."),
|
||||
nls.localize('scope.window.description', "Window specific configuration, which can be configured in the User or Workspace settings."),
|
||||
nls.localize('scope.resource.description', "Resource specific configuration, which can be configured in the User, Workspace or Folder settings.")
|
||||
],
|
||||
description: nls.localize('scope.description', "Scope in which the configuration is applicable. Available scopes are `window` and `resource`.")
|
||||
},
|
||||
enumDescriptions: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'string',
|
||||
},
|
||||
description: nls.localize('scope.enumDescriptions', 'Descriptions for enum values')
|
||||
},
|
||||
markdownEnumDescription: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'string',
|
||||
},
|
||||
description: nls.localize('scope.markdownEnumDescription', 'Descriptions for enum values in the markdown format.')
|
||||
},
|
||||
markdownDescription: {
|
||||
type: 'string',
|
||||
description: nls.localize('scope.markdownDescription', 'The description in the markdown format.')
|
||||
},
|
||||
deprecationMessage: {
|
||||
type: 'string',
|
||||
description: nls.localize('scope.deprecationMessage', 'If set, the property is marked as deprecated and the given message is shown as an explanation.')
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let registeredDefaultConfigurations: IDefaultConfigurationExtension[] = [];
|
||||
|
||||
// BEGIN VSCode extension point `configurationDefaults`
|
||||
const defaultConfigurationExtPoint = ExtensionsRegistry.registerExtensionPoint<IConfigurationNode>('configurationDefaults', [], {
|
||||
description: nls.localize('vscode.extension.contributes.defaultConfiguration', 'Contributes default editor configuration settings by language.'),
|
||||
type: 'object',
|
||||
defaultSnippets: [{ body: {} }],
|
||||
patternProperties: {
|
||||
'\\[.*\\]$': {
|
||||
type: 'object',
|
||||
default: {},
|
||||
$ref: editorConfigurationSchemaId,
|
||||
}
|
||||
}
|
||||
});
|
||||
defaultConfigurationExtPoint.setHandler(extensions => {
|
||||
registeredDefaultConfigurations = extensions.map(extension => {
|
||||
const id = extension.description.id;
|
||||
const name = extension.description.name;
|
||||
const defaults = objects.deepClone(extension.value);
|
||||
return <IDefaultConfigurationExtension>{
|
||||
id, name, defaults
|
||||
};
|
||||
});
|
||||
});
|
||||
// END VSCode extension point `configurationDefaults`
|
||||
|
||||
|
||||
// BEGIN VSCode extension point `configuration`
|
||||
const configurationExtPoint = ExtensionsRegistry.registerExtensionPoint<IConfigurationNode>('configuration', [defaultConfigurationExtPoint], {
|
||||
description: nls.localize('vscode.extension.contributes.configuration', 'Contributes configuration settings.'),
|
||||
oneOf: [
|
||||
configurationEntrySchema,
|
||||
{
|
||||
type: 'array',
|
||||
items: configurationEntrySchema
|
||||
}
|
||||
]
|
||||
});
|
||||
configurationExtPoint.setHandler(extensions => {
|
||||
const configurations: IConfigurationNode[] = [];
|
||||
|
||||
function handleConfiguration(node: IConfigurationNode, extension: IExtensionPointUser<any>) {
|
||||
let configuration = objects.deepClone(node);
|
||||
|
||||
if (configuration.title && (typeof configuration.title !== 'string')) {
|
||||
extension.collector.error(nls.localize('invalid.title', "'configuration.title' must be a string"));
|
||||
}
|
||||
|
||||
validateProperties(configuration, extension);
|
||||
|
||||
configuration.id = node.id || extension.description.id || extension.description.uuid;
|
||||
configuration.contributedByExtension = true;
|
||||
configuration.title = configuration.title || extension.description.displayName || extension.description.id;
|
||||
configurations.push(configuration);
|
||||
}
|
||||
|
||||
for (let extension of extensions) {
|
||||
const value = <IConfigurationNode | IConfigurationNode[]>extension.value;
|
||||
if (!Array.isArray(value)) {
|
||||
handleConfiguration(value, extension);
|
||||
} else {
|
||||
value.forEach(v => handleConfiguration(v, extension));
|
||||
}
|
||||
}
|
||||
configurationRegistry.registerConfigurations(configurations, registeredDefaultConfigurations, false);
|
||||
});
|
||||
// END VSCode extension point `configuration`
|
||||
|
||||
function validateProperties(configuration: IConfigurationNode, extension: IExtensionPointUser<any>): void {
|
||||
let properties = configuration.properties;
|
||||
if (properties) {
|
||||
if (typeof properties !== 'object') {
|
||||
extension.collector.error(nls.localize('invalid.properties', "'configuration.properties' must be an object"));
|
||||
configuration.properties = {};
|
||||
}
|
||||
for (let key in properties) {
|
||||
const message = validateProperty(key);
|
||||
if (message) {
|
||||
delete properties[key];
|
||||
extension.collector.warn(message);
|
||||
continue;
|
||||
}
|
||||
const propertyConfiguration = properties[key];
|
||||
if (!isObject(propertyConfiguration)) {
|
||||
delete properties[key];
|
||||
extension.collector.error(nls.localize('invalid.property', "'configuration.property' must be an object"));
|
||||
continue;
|
||||
}
|
||||
if (propertyConfiguration.scope) {
|
||||
if (propertyConfiguration.scope.toString() === 'application') {
|
||||
propertyConfiguration.scope = ConfigurationScope.APPLICATION;
|
||||
} else if (propertyConfiguration.scope.toString() === 'resource') {
|
||||
propertyConfiguration.scope = ConfigurationScope.RESOURCE;
|
||||
} else {
|
||||
propertyConfiguration.scope = ConfigurationScope.WINDOW;
|
||||
}
|
||||
} else {
|
||||
propertyConfiguration.scope = ConfigurationScope.WINDOW;
|
||||
}
|
||||
}
|
||||
}
|
||||
let subNodes = configuration.allOf;
|
||||
if (subNodes) {
|
||||
extension.collector.error(nls.localize('invalid.allOf', "'configuration.allOf' is deprecated and should no longer be used. Instead, pass multiple configuration sections as an array to the 'configuration' contribution point."));
|
||||
for (let node of subNodes) {
|
||||
validateProperties(node, extension);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const jsonRegistry = Registry.as<IJSONContributionRegistry>(JSONExtensions.JSONContribution);
|
||||
jsonRegistry.registerSchema('vscode://schemas/workspaceConfig', {
|
||||
allowComments: true,
|
||||
default: {
|
||||
folders: [
|
||||
{
|
||||
path: ''
|
||||
}
|
||||
],
|
||||
settings: {
|
||||
}
|
||||
},
|
||||
required: ['folders'],
|
||||
properties: {
|
||||
'folders': {
|
||||
minItems: 0,
|
||||
uniqueItems: true,
|
||||
description: nls.localize('workspaceConfig.folders.description', "List of folders to be loaded in the workspace."),
|
||||
items: {
|
||||
type: 'object',
|
||||
default: { path: '' },
|
||||
oneOf: [{
|
||||
properties: {
|
||||
path: {
|
||||
type: 'string',
|
||||
description: nls.localize('workspaceConfig.path.description', "A file path. e.g. `/root/folderA` or `./folderA` for a relative path that will be resolved against the location of the workspace file.")
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
description: nls.localize('workspaceConfig.name.description', "An optional name for the folder. ")
|
||||
}
|
||||
},
|
||||
required: ['path']
|
||||
}, {
|
||||
properties: {
|
||||
uri: {
|
||||
type: 'string',
|
||||
description: nls.localize('workspaceConfig.uri.description', "URI of the folder")
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
description: nls.localize('workspaceConfig.name.description', "An optional name for the folder. ")
|
||||
}
|
||||
},
|
||||
required: ['uri']
|
||||
}]
|
||||
}
|
||||
},
|
||||
'settings': {
|
||||
type: 'object',
|
||||
default: {},
|
||||
description: nls.localize('workspaceConfig.settings.description', "Workspace settings"),
|
||||
$ref: workspaceSettingsSchemaId
|
||||
},
|
||||
'launch': {
|
||||
type: 'object',
|
||||
default: { configurations: [], compounds: [] },
|
||||
description: nls.localize('workspaceConfig.launch.description', "Workspace launch configurations"),
|
||||
$ref: launchSchemaId
|
||||
},
|
||||
'extensions': {
|
||||
type: 'object',
|
||||
default: {},
|
||||
description: nls.localize('workspaceConfig.extensions.description', "Workspace extensions"),
|
||||
$ref: 'vscode://schemas/extensions'
|
||||
}
|
||||
},
|
||||
additionalProperties: false,
|
||||
errorMessage: nls.localize('unknownWorkspaceProperty', "Unknown workspace configuration property")
|
||||
});
|
||||
@@ -202,7 +202,11 @@ export class Configuration extends BaseConfiguration {
|
||||
// Do not remove workspace configuration
|
||||
return new ConfigurationChangeEvent();
|
||||
}
|
||||
const keys = this.folders.get(folder).keys;
|
||||
const folderConfig = this.folders.get(folder);
|
||||
if (!folderConfig) {
|
||||
throw new Error('Unknown folder');
|
||||
}
|
||||
const keys = folderConfig.keys;
|
||||
super.deleteFolderConfiguration(folder);
|
||||
return new ConfigurationChangeEvent().change(keys, folder);
|
||||
}
|
||||
|
||||
@@ -15,11 +15,10 @@ import { Disposable, IDisposable, dispose } 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 { isLinux } from 'vs/base/common/platform';
|
||||
import { ConfigWatcher } from 'vs/base/node/config';
|
||||
import { ConfigurationModel } 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 { IStoredWorkspace, IStoredWorkspaceFolder } from 'vs/platform/workspaces/common/workspaces';
|
||||
import { IStoredWorkspaceFolder } from 'vs/platform/workspaces/common/workspaces';
|
||||
import * as extfs from 'vs/base/node/extfs';
|
||||
import { JSONEditingService } from 'vs/workbench/services/configuration/node/jsonEditingService';
|
||||
import { WorkbenchState, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
|
||||
@@ -30,96 +29,287 @@ import { Schemas } from 'vs/base/common/network';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { IConfigurationModel } from 'vs/platform/configuration/common/configuration';
|
||||
|
||||
export interface IWorkspaceIdentifier {
|
||||
id: string;
|
||||
configPath: URI;
|
||||
}
|
||||
|
||||
export class WorkspaceConfiguration extends Disposable {
|
||||
|
||||
private _workspaceConfigPath: URI;
|
||||
private _workspaceConfigurationWatcher: ConfigWatcher<WorkspaceConfigurationModelParser>;
|
||||
private _workspaceConfigurationWatcherDisposables: IDisposable[] = [];
|
||||
private readonly _cachedConfiguration: CachedWorkspaceConfiguration;
|
||||
private _workspaceConfiguration: IWorkspaceConfiguration | null;
|
||||
private _workspaceIdentifier: IWorkspaceIdentifier | null = null;
|
||||
private _fileService: IFileService | null = null;
|
||||
|
||||
private readonly _onDidUpdateConfiguration: Emitter<void> = this._register(new Emitter<void>());
|
||||
public readonly onDidUpdateConfiguration: Event<void> = this._onDidUpdateConfiguration.event;
|
||||
|
||||
private _workspaceConfigurationModelParser: WorkspaceConfigurationModelParser = new WorkspaceConfigurationModelParser(this._workspaceConfigPath ? this._workspaceConfigPath.fsPath : '');
|
||||
private _cache: ConfigurationModel = new ConfigurationModel();
|
||||
constructor(
|
||||
environmentService: IEnvironmentService
|
||||
) {
|
||||
super();
|
||||
this._cachedConfiguration = new CachedWorkspaceConfiguration(environmentService);
|
||||
this._workspaceConfiguration = this._cachedConfiguration;
|
||||
}
|
||||
|
||||
load(workspaceConfigPath: URI): Promise<void> {
|
||||
if (this._workspaceConfigPath && this._workspaceConfigPath.fsPath === workspaceConfigPath.fsPath) {
|
||||
return this.reload();
|
||||
}
|
||||
|
||||
this._workspaceConfigPath = workspaceConfigPath;
|
||||
|
||||
return new Promise<void>((c, e) => {
|
||||
const defaultConfig = new WorkspaceConfigurationModelParser(this._workspaceConfigPath.fsPath);
|
||||
defaultConfig.parse(JSON.stringify({ folders: [] } as IStoredWorkspace, null, '\t'));
|
||||
if (this._workspaceConfigurationWatcher) {
|
||||
this.disposeConfigurationWatcher();
|
||||
adopt(fileService: IFileService): Promise<boolean> {
|
||||
if (!this._fileService) {
|
||||
this._fileService = fileService;
|
||||
if (this.adoptWorkspaceConfiguration()) {
|
||||
if (this._workspaceIdentifier) {
|
||||
return this._workspaceConfiguration.load(this._workspaceIdentifier).then(() => true);
|
||||
}
|
||||
}
|
||||
this._workspaceConfigurationWatcher = new ConfigWatcher(this._workspaceConfigPath.fsPath, {
|
||||
changeBufferDelay: 300,
|
||||
onError: error => errors.onUnexpectedError(error),
|
||||
defaultConfig,
|
||||
parse: (content: string, parseErrors: any[]) => {
|
||||
this._workspaceConfigurationModelParser = new WorkspaceConfigurationModelParser(this._workspaceConfigPath.fsPath);
|
||||
this._workspaceConfigurationModelParser.parse(content);
|
||||
parseErrors = [...this._workspaceConfigurationModelParser.errors];
|
||||
this.consolidate();
|
||||
return this._workspaceConfigurationModelParser;
|
||||
}, initCallback: () => c(null)
|
||||
});
|
||||
this.listenToWatcher();
|
||||
});
|
||||
}
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
load(workspaceIdentifier: IWorkspaceIdentifier): Promise<void> {
|
||||
this._workspaceIdentifier = workspaceIdentifier;
|
||||
this.adoptWorkspaceConfiguration();
|
||||
return this._workspaceConfiguration.load(this._workspaceIdentifier);
|
||||
}
|
||||
|
||||
reload(): Promise<void> {
|
||||
this.stopListeningToWatcher();
|
||||
return new Promise<void>(c => this._workspaceConfigurationWatcher.reload(() => {
|
||||
this.listenToWatcher();
|
||||
c(null);
|
||||
}));
|
||||
return this._workspaceIdentifier ? this.load(this._workspaceIdentifier) : Promise.resolve();
|
||||
}
|
||||
|
||||
getFolders(): IStoredWorkspaceFolder[] {
|
||||
return this._workspaceConfiguration.getFolders();
|
||||
}
|
||||
|
||||
setFolders(folders: IStoredWorkspaceFolder[], jsonEditingService: JSONEditingService): Promise<void> {
|
||||
if (this._workspaceIdentifier) {
|
||||
return jsonEditingService.write(this._workspaceIdentifier.configPath, { key: 'folders', value: folders }, true)
|
||||
.then(() => this.reload());
|
||||
}
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
getConfiguration(): ConfigurationModel {
|
||||
return this._workspaceConfiguration.getWorkspaceSettings();
|
||||
}
|
||||
|
||||
reprocessWorkspaceSettings(): ConfigurationModel {
|
||||
this._workspaceConfiguration.reprocessWorkspaceSettings();
|
||||
return this.getConfiguration();
|
||||
}
|
||||
|
||||
private adoptWorkspaceConfiguration(): boolean {
|
||||
if (this._workspaceIdentifier) {
|
||||
if (this._fileService) {
|
||||
if (!(this._workspaceConfiguration instanceof FileServiceBasedWorkspaceConfiguration)) {
|
||||
dispose(this._workspaceConfiguration);
|
||||
const nodeBasedWorkspaceConfiguration = this._workspaceConfiguration instanceof NodeBasedWorkspaceConfiguration ? this._workspaceConfiguration : undefined;
|
||||
this._workspaceConfiguration = new FileServiceBasedWorkspaceConfiguration(this._fileService, nodeBasedWorkspaceConfiguration);
|
||||
this._register(this._workspaceConfiguration.onDidChange(e => this.onDidWorkspaceConfigurationChange()));
|
||||
return !nodeBasedWorkspaceConfiguration;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (this._workspaceIdentifier.configPath.scheme === Schemas.file) {
|
||||
if (!(this._workspaceConfiguration instanceof NodeBasedWorkspaceConfiguration)) {
|
||||
dispose(this._workspaceConfiguration);
|
||||
this._workspaceConfiguration = new NodeBasedWorkspaceConfiguration();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private onDidWorkspaceConfigurationChange(): void {
|
||||
this.updateCache();
|
||||
this.reload().then(() => this._onDidUpdateConfiguration.fire());
|
||||
}
|
||||
|
||||
private updateCache(): Promise<void> {
|
||||
if (this._workspaceIdentifier && this._workspaceIdentifier.configPath.scheme !== Schemas.file && this._workspaceConfiguration instanceof FileServiceBasedWorkspaceConfiguration) {
|
||||
return this._workspaceConfiguration.load(this._workspaceIdentifier)
|
||||
.then(() => this._cachedConfiguration.updateWorkspace(this._workspaceIdentifier, this._workspaceConfiguration.getConfigurationModel()));
|
||||
}
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
}
|
||||
|
||||
interface IWorkspaceConfiguration extends IDisposable {
|
||||
readonly onDidChange: Event<void>;
|
||||
load(workspaceIdentifier: IWorkspaceIdentifier): Promise<void>;
|
||||
getConfigurationModel(): ConfigurationModel;
|
||||
getFolders(): IStoredWorkspaceFolder[];
|
||||
getWorkspaceSettings(): ConfigurationModel;
|
||||
reprocessWorkspaceSettings(): ConfigurationModel;
|
||||
}
|
||||
|
||||
abstract class AbstractWorkspaceConfiguration extends Disposable implements IWorkspaceConfiguration {
|
||||
|
||||
private _workspaceConfigurationModelParser: WorkspaceConfigurationModelParser;
|
||||
private _workspaceSettings: ConfigurationModel;
|
||||
private _workspaceIdentifier: IWorkspaceIdentifier | null = null;
|
||||
|
||||
protected readonly _onDidChange: Emitter<void> = this._register(new Emitter<void>());
|
||||
readonly onDidChange: Event<void> = this._onDidChange.event;
|
||||
|
||||
constructor(from?: AbstractWorkspaceConfiguration) {
|
||||
super();
|
||||
|
||||
this._workspaceConfigurationModelParser = from ? from._workspaceConfigurationModelParser : new WorkspaceConfigurationModelParser('');
|
||||
this._workspaceSettings = new ConfigurationModel();
|
||||
}
|
||||
|
||||
get workspaceIdentifier(): IWorkspaceIdentifier | null {
|
||||
return this._workspaceIdentifier;
|
||||
}
|
||||
|
||||
load(workspaceIdentifier: IWorkspaceIdentifier): Promise<void> {
|
||||
this._workspaceIdentifier = workspaceIdentifier;
|
||||
return this.loadWorkspaceConfigurationContents(workspaceIdentifier)
|
||||
.then(contents => {
|
||||
this._workspaceConfigurationModelParser = new WorkspaceConfigurationModelParser(workspaceIdentifier.id);
|
||||
this._workspaceConfigurationModelParser.parse(contents);
|
||||
this.consolidate();
|
||||
});
|
||||
}
|
||||
|
||||
getConfigurationModel(): ConfigurationModel {
|
||||
return this._workspaceConfigurationModelParser.configurationModel;
|
||||
}
|
||||
|
||||
getFolders(): IStoredWorkspaceFolder[] {
|
||||
return this._workspaceConfigurationModelParser.folders;
|
||||
}
|
||||
|
||||
setFolders(folders: IStoredWorkspaceFolder[], jsonEditingService: JSONEditingService): Promise<void> {
|
||||
return jsonEditingService.write(this._workspaceConfigPath, { key: 'folders', value: folders }, true)
|
||||
.then(() => this.reload());
|
||||
}
|
||||
|
||||
getConfiguration(): ConfigurationModel {
|
||||
return this._cache;
|
||||
getWorkspaceSettings(): ConfigurationModel {
|
||||
return this._workspaceSettings;
|
||||
}
|
||||
|
||||
reprocessWorkspaceSettings(): ConfigurationModel {
|
||||
this._workspaceConfigurationModelParser.reprocessWorkspaceSettings();
|
||||
this.consolidate();
|
||||
return this.getConfiguration();
|
||||
}
|
||||
|
||||
private listenToWatcher() {
|
||||
this._workspaceConfigurationWatcher.onDidUpdateConfiguration(() => this._onDidUpdateConfiguration.fire(), this, this._workspaceConfigurationWatcherDisposables);
|
||||
}
|
||||
|
||||
private stopListeningToWatcher() {
|
||||
this._workspaceConfigurationWatcherDisposables = dispose(this._workspaceConfigurationWatcherDisposables);
|
||||
return this.getWorkspaceSettings();
|
||||
}
|
||||
|
||||
private consolidate(): void {
|
||||
this._cache = this._workspaceConfigurationModelParser.settingsModel.merge(this._workspaceConfigurationModelParser.launchModel);
|
||||
this._workspaceSettings = this._workspaceConfigurationModelParser.settingsModel.merge(this._workspaceConfigurationModelParser.launchModel);
|
||||
}
|
||||
|
||||
private disposeConfigurationWatcher(): void {
|
||||
this.stopListeningToWatcher();
|
||||
if (this._workspaceConfigurationWatcher) {
|
||||
this._workspaceConfigurationWatcher.dispose();
|
||||
protected abstract loadWorkspaceConfigurationContents(workspaceIdentifier: IWorkspaceIdentifier): Promise<string>;
|
||||
}
|
||||
|
||||
class NodeBasedWorkspaceConfiguration extends AbstractWorkspaceConfiguration {
|
||||
|
||||
protected loadWorkspaceConfigurationContents(workspaceIdentifier: IWorkspaceIdentifier): Promise<string> {
|
||||
return pfs.readFile(workspaceIdentifier.configPath.fsPath)
|
||||
.then(contents => contents.toString(), e => {
|
||||
errors.onUnexpectedError(e);
|
||||
return '';
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class FileServiceBasedWorkspaceConfiguration extends AbstractWorkspaceConfiguration {
|
||||
|
||||
private workspaceConfig: URI | null = null;
|
||||
private readonly reloadConfigurationScheduler: RunOnceScheduler;
|
||||
|
||||
constructor(private fileService: IFileService, from?: AbstractWorkspaceConfiguration) {
|
||||
super(from);
|
||||
this.workspaceConfig = from ? from.workspaceIdentifier.configPath : null;
|
||||
this._register(fileService.onFileChanges(e => this.handleWorkspaceFileEvents(e)));
|
||||
this.reloadConfigurationScheduler = this._register(new RunOnceScheduler(() => this._onDidChange.fire(), 50));
|
||||
}
|
||||
|
||||
protected loadWorkspaceConfigurationContents(workspaceIdentifier: IWorkspaceIdentifier): Promise<string> {
|
||||
this.workspaceConfig = workspaceIdentifier.configPath;
|
||||
return this.fileService.resolveContent(this.workspaceConfig)
|
||||
.then(content => content.value, e => {
|
||||
errors.onUnexpectedError(e);
|
||||
return '';
|
||||
});
|
||||
}
|
||||
|
||||
private handleWorkspaceFileEvents(event: FileChangesEvent): void {
|
||||
if (this.workspaceConfig) {
|
||||
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.workspaceConfig, events[i].resource);
|
||||
}
|
||||
|
||||
if (affectedByChanges) {
|
||||
this.reloadConfigurationScheduler.schedule();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class CachedWorkspaceConfiguration extends Disposable implements IWorkspaceConfiguration {
|
||||
|
||||
private readonly _onDidChange: Emitter<void> = this._register(new Emitter<void>());
|
||||
readonly onDidChange: Event<void> = this._onDidChange.event;
|
||||
|
||||
private cachedWorkspacePath: string;
|
||||
private cachedConfigurationPath: string;
|
||||
private _workspaceConfigurationModelParser: WorkspaceConfigurationModelParser;
|
||||
private _workspaceSettings: ConfigurationModel;
|
||||
|
||||
constructor(private environmentService: IEnvironmentService) {
|
||||
super();
|
||||
this._workspaceConfigurationModelParser = new WorkspaceConfigurationModelParser('');
|
||||
this._workspaceSettings = new ConfigurationModel();
|
||||
}
|
||||
|
||||
load(workspaceIdentifier: IWorkspaceIdentifier): Promise<void> {
|
||||
this.createPaths(workspaceIdentifier);
|
||||
return pfs.readFile(this.cachedConfigurationPath)
|
||||
.then(contents => {
|
||||
this._workspaceConfigurationModelParser = new WorkspaceConfigurationModelParser(this.cachedConfigurationPath);
|
||||
this._workspaceConfigurationModelParser.parse(contents.toString());
|
||||
this._workspaceSettings = this._workspaceConfigurationModelParser.settingsModel.merge(this._workspaceConfigurationModelParser.launchModel);
|
||||
}, () => null);
|
||||
}
|
||||
|
||||
getConfigurationModel(): ConfigurationModel {
|
||||
return this._workspaceConfigurationModelParser.configurationModel;
|
||||
}
|
||||
|
||||
getFolders(): IStoredWorkspaceFolder[] {
|
||||
return this._workspaceConfigurationModelParser.folders;
|
||||
}
|
||||
|
||||
getWorkspaceSettings(): ConfigurationModel {
|
||||
return this._workspaceSettings;
|
||||
}
|
||||
|
||||
reprocessWorkspaceSettings(): ConfigurationModel {
|
||||
return this._workspaceSettings;
|
||||
}
|
||||
|
||||
async updateWorkspace(workspaceIdentifier: IWorkspaceIdentifier, configurationModel: ConfigurationModel): Promise<void> {
|
||||
try {
|
||||
this.createPaths(workspaceIdentifier);
|
||||
if (configurationModel.keys.length) {
|
||||
const exists = await pfs.exists(this.cachedWorkspacePath);
|
||||
if (!exists) {
|
||||
await pfs.mkdirp(this.cachedWorkspacePath);
|
||||
}
|
||||
const raw = JSON.stringify(configurationModel.toJSON());
|
||||
await pfs.writeFile(this.cachedConfigurationPath, raw);
|
||||
} else {
|
||||
pfs.rimraf(this.cachedWorkspacePath);
|
||||
}
|
||||
} catch (error) {
|
||||
errors.onUnexpectedError(error);
|
||||
}
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.disposeConfigurationWatcher();
|
||||
super.dispose();
|
||||
private createPaths(workspaceIdentifier: IWorkspaceIdentifier) {
|
||||
this.cachedWorkspacePath = paths.join(this.environmentService.userDataPath, 'CachedConfigurations', 'workspaces', workspaceIdentifier.id);
|
||||
this.cachedConfigurationPath = paths.join(this.cachedWorkspacePath, 'workspace.json');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,12 +323,11 @@ function isFolderSettingsConfigurationFile(resource: URI): boolean {
|
||||
return resources.isEqual(URI.from({ scheme: resource.scheme, path: resources.basename(resource) }), URI.from({ scheme: resource.scheme, path: `${FOLDER_SETTINGS_NAME}.json` }));
|
||||
}
|
||||
|
||||
export interface IFolderConfiguration {
|
||||
export interface IFolderConfiguration extends IDisposable {
|
||||
readonly onDidChange: Event<void>;
|
||||
readonly loaded: boolean;
|
||||
loadConfiguration(): Promise<ConfigurationModel>;
|
||||
reprocess(): ConfigurationModel;
|
||||
dispose(): void;
|
||||
}
|
||||
|
||||
export abstract class AbstractFolderConfiguration extends Disposable implements IFolderConfiguration {
|
||||
@@ -225,13 +414,16 @@ export class NodeBasedFolderConfiguration extends AbstractFolderConfiguration {
|
||||
|
||||
protected loadFolderConfigurationContents(): Promise<{ resource: URI, value: string }[]> {
|
||||
return this.resolveStat(this.folderConfigurationPath).then(stat => {
|
||||
if (!stat.isDirectory) {
|
||||
if (!stat.isDirectory || !stat.children) {
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
return this.resolveContents(stat.children.filter(stat => isFolderConfigurationFile(stat.resource))
|
||||
.map(stat => stat.resource));
|
||||
}, err => [] /* never fail this call */)
|
||||
.then(null, errors.onUnexpectedError);
|
||||
.then(undefined, e => {
|
||||
errors.onUnexpectedError(e);
|
||||
return [];
|
||||
});
|
||||
}
|
||||
|
||||
private resolveContents(resources: URI[]): Promise<{ resource: URI, value: string }[]> {
|
||||
@@ -265,7 +457,7 @@ export class FileServiceBasedFolderConfiguration extends AbstractFolderConfigura
|
||||
|
||||
private reloadConfigurationScheduler: RunOnceScheduler;
|
||||
private readonly folderConfigurationPath: URI;
|
||||
private readonly loadConfigurationDelayer: Delayer<{ resource: URI, value: string }[]> = new Delayer<{ resource: URI, value: string }[]>(50);
|
||||
private readonly loadConfigurationDelayer = new Delayer<Array<{ resource: URI, value: string }>>(50);
|
||||
|
||||
constructor(folder: URI, private configFolderRelativePath: string, workbenchState: WorkbenchState, private fileService: IFileService, from?: AbstractFolderConfiguration) {
|
||||
super(folder, workbenchState, from);
|
||||
@@ -274,20 +466,25 @@ export class FileServiceBasedFolderConfiguration extends AbstractFolderConfigura
|
||||
this._register(fileService.onFileChanges(e => this.handleWorkspaceFileEvents(e)));
|
||||
}
|
||||
|
||||
protected loadFolderConfigurationContents(): Promise<{ resource: URI, value: string }[]> {
|
||||
protected loadFolderConfigurationContents(): Promise<Array<{ resource: URI, value: string }>> {
|
||||
return Promise.resolve(this.loadConfigurationDelayer.trigger(() => this.doLoadFolderConfigurationContents()));
|
||||
}
|
||||
|
||||
private doLoadFolderConfigurationContents(): Promise<{ resource: URI, value: string }[]> {
|
||||
private doLoadFolderConfigurationContents(): Promise<Array<{ resource: URI, value: string }>> {
|
||||
const workspaceFilePathToConfiguration: { [relativeWorkspacePath: string]: Promise<IContent> } = Object.create(null);
|
||||
const bulkContentFetchromise = Promise.resolve(this.fileService.resolveFile(this.folderConfigurationPath))
|
||||
.then(stat => {
|
||||
if (stat.isDirectory && stat.children) {
|
||||
stat.children
|
||||
.filter(child => isFolderConfigurationFile(child.resource))
|
||||
.forEach(child => workspaceFilePathToConfiguration[this.toFolderRelativePath(child.resource)] = Promise.resolve(this.fileService.resolveContent(child.resource)).then(null, errors.onUnexpectedError));
|
||||
.forEach(child => {
|
||||
const folderRelativePath = this.toFolderRelativePath(child.resource);
|
||||
if (folderRelativePath) {
|
||||
workspaceFilePathToConfiguration[folderRelativePath] = Promise.resolve(this.fileService.resolveContent(child.resource)).then(undefined, errors.onUnexpectedError);
|
||||
}
|
||||
});
|
||||
}
|
||||
}).then(null, err => [] /* never fail this call */);
|
||||
}).then(undefined, err => [] /* never fail this call */);
|
||||
|
||||
return bulkContentFetchromise.then(() => Promise.all(collections.values(workspaceFilePathToConfiguration)));
|
||||
}
|
||||
@@ -298,7 +495,6 @@ export class FileServiceBasedFolderConfiguration extends AbstractFolderConfigura
|
||||
|
||||
// Find changes that affect workspace configuration files
|
||||
for (let i = 0, len = events.length; i < len; i++) {
|
||||
|
||||
const resource = events[i].resource;
|
||||
const basename = resources.basename(resource);
|
||||
const isJson = paths.extname(basename) === '.json';
|
||||
@@ -316,6 +512,7 @@ export class FileServiceBasedFolderConfiguration extends AbstractFolderConfigura
|
||||
// Handle case where ".vscode" got deleted
|
||||
if (isDeletedSettingsFolder) {
|
||||
affectedByChanges = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// only valid workspace config files
|
||||
@@ -323,12 +520,8 @@ export class FileServiceBasedFolderConfiguration extends AbstractFolderConfigura
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (events[i].type) {
|
||||
case FileChangeType.DELETED:
|
||||
case FileChangeType.UPDATED:
|
||||
case FileChangeType.ADDED:
|
||||
affectedByChanges = true;
|
||||
}
|
||||
affectedByChanges = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (affectedByChanges) {
|
||||
@@ -336,7 +529,7 @@ export class FileServiceBasedFolderConfiguration extends AbstractFolderConfigura
|
||||
}
|
||||
}
|
||||
|
||||
private toFolderRelativePath(resource: URI): string {
|
||||
private toFolderRelativePath(resource: URI): string | null {
|
||||
if (resource.scheme === Schemas.file) {
|
||||
if (paths.isEqualOrParent(resource.fsPath, this.folderConfigurationPath.fsPath, !isLinux /* ignorecase */)) {
|
||||
return paths.normalize(relative(this.folderConfigurationPath.fsPath, resource.fsPath));
|
||||
@@ -366,7 +559,7 @@ export class CachedFolderConfiguration extends Disposable implements IFolderConf
|
||||
configFolderRelativePath: string,
|
||||
environmentService: IEnvironmentService) {
|
||||
super();
|
||||
this.cachedFolderPath = paths.join(environmentService.appSettingsHome, createHash('md5').update(paths.join(folder.path, configFolderRelativePath)).digest('hex'));
|
||||
this.cachedFolderPath = paths.join(environmentService.userDataPath, 'CachedConfigurations', 'folders', createHash('md5').update(paths.join(folder.path, configFolderRelativePath)).digest('hex'));
|
||||
this.cachedConfigurationPath = paths.join(this.cachedFolderPath, 'configuration.json');
|
||||
this.configurationModel = new ConfigurationModel();
|
||||
}
|
||||
@@ -387,7 +580,7 @@ export class CachedFolderConfiguration extends Disposable implements IFolderConf
|
||||
if (created) {
|
||||
return configurationModel.keys.length ? pfs.writeFile(this.cachedConfigurationPath, raw) : pfs.rimraf(this.cachedFolderPath);
|
||||
}
|
||||
return null;
|
||||
return undefined;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -401,7 +594,7 @@ export class CachedFolderConfiguration extends Disposable implements IFolderConf
|
||||
|
||||
private createCachedFolder(): Promise<boolean> {
|
||||
return Promise.resolve(pfs.exists(this.cachedFolderPath))
|
||||
.then(null, () => false)
|
||||
.then(undefined, () => false)
|
||||
.then(exists => exists ? exists : pfs.mkdirp(this.cachedFolderPath).then(() => true, () => false));
|
||||
}
|
||||
}
|
||||
@@ -492,6 +685,6 @@ export class FolderConfiguration extends Disposable implements IFolderConfigurat
|
||||
return this.folderConfiguration.loadConfiguration()
|
||||
.then(configurationModel => this.cachedFolderConfiguration.updateConfiguration(configurationModel));
|
||||
}
|
||||
return Promise.resolve(null);
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,15 +122,15 @@ export class ConfigurationEditingService {
|
||||
private queue: Queue<void>;
|
||||
|
||||
constructor(
|
||||
@IConfigurationService private configurationService: IConfigurationService,
|
||||
@IWorkspaceContextService private contextService: IWorkspaceContextService,
|
||||
@IEnvironmentService private environmentService: IEnvironmentService,
|
||||
@IFileService private fileService: IFileService,
|
||||
@ITextModelService private textModelResolverService: ITextModelService,
|
||||
@ITextFileService private textFileService: ITextFileService,
|
||||
@INotificationService private notificationService: INotificationService,
|
||||
@IPreferencesService private preferencesService: IPreferencesService,
|
||||
@IEditorService private editorService: IEditorService
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService,
|
||||
@IWorkspaceContextService private readonly contextService: IWorkspaceContextService,
|
||||
@IEnvironmentService private readonly environmentService: IEnvironmentService,
|
||||
@IFileService private readonly fileService: IFileService,
|
||||
@ITextModelService private readonly textModelResolverService: ITextModelService,
|
||||
@ITextFileService private readonly textFileService: ITextFileService,
|
||||
@INotificationService private readonly notificationService: INotificationService,
|
||||
@IPreferencesService private readonly preferencesService: IPreferencesService,
|
||||
@IEditorService private readonly editorService: IEditorService
|
||||
) {
|
||||
this.queue = new Queue<void>();
|
||||
}
|
||||
@@ -264,7 +264,7 @@ export class ConfigurationEditingService {
|
||||
this.editorService.openEditor({ resource });
|
||||
}
|
||||
|
||||
private wrapError<T = never>(code: ConfigurationEditingErrorCode, target: ConfigurationTarget, operation: IConfigurationEditOperation): Promise<T> {
|
||||
private reject<T = never>(code: ConfigurationEditingErrorCode, target: ConfigurationTarget, operation: IConfigurationEditOperation): Promise<T> {
|
||||
const message = this.toErrorMessage(code, target, operation);
|
||||
|
||||
return Promise.reject(new ConfigurationEditingError(message, code));
|
||||
@@ -377,45 +377,45 @@ export class ConfigurationEditingService {
|
||||
if (!operation.workspaceStandAloneConfigurationKey) {
|
||||
const validKeys = this.configurationService.keys().default;
|
||||
if (validKeys.indexOf(operation.key) < 0 && !OVERRIDE_PROPERTY_PATTERN.test(operation.key)) {
|
||||
return this.wrapError(ConfigurationEditingErrorCode.ERROR_UNKNOWN_KEY, target, operation);
|
||||
return this.reject(ConfigurationEditingErrorCode.ERROR_UNKNOWN_KEY, target, operation);
|
||||
}
|
||||
}
|
||||
|
||||
if (operation.workspaceStandAloneConfigurationKey) {
|
||||
// Global tasks and launches are not supported
|
||||
if (target === ConfigurationTarget.USER) {
|
||||
return this.wrapError(ConfigurationEditingErrorCode.ERROR_INVALID_USER_TARGET, target, operation);
|
||||
return this.reject(ConfigurationEditingErrorCode.ERROR_INVALID_USER_TARGET, target, operation);
|
||||
}
|
||||
|
||||
// Workspace tasks are not supported
|
||||
if (operation.workspaceStandAloneConfigurationKey === TASKS_CONFIGURATION_KEY && this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE && operation.target === ConfigurationTarget.WORKSPACE) {
|
||||
return this.wrapError(ConfigurationEditingErrorCode.ERROR_INVALID_WORKSPACE_TARGET, target, operation);
|
||||
return this.reject(ConfigurationEditingErrorCode.ERROR_INVALID_WORKSPACE_TARGET, target, operation);
|
||||
}
|
||||
}
|
||||
|
||||
// Target cannot be workspace or folder if no workspace opened
|
||||
if ((target === ConfigurationTarget.WORKSPACE || target === ConfigurationTarget.WORKSPACE_FOLDER) && this.contextService.getWorkbenchState() === WorkbenchState.EMPTY) {
|
||||
return this.wrapError(ConfigurationEditingErrorCode.ERROR_NO_WORKSPACE_OPENED, target, operation);
|
||||
return this.reject(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);
|
||||
return this.reject(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);
|
||||
return this.reject(ConfigurationEditingErrorCode.ERROR_INVALID_FOLDER_TARGET, target, operation);
|
||||
}
|
||||
|
||||
if (!operation.workspaceStandAloneConfigurationKey) {
|
||||
const configurationProperties = Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration).getConfigurationProperties();
|
||||
if (configurationProperties[operation.key].scope !== ConfigurationScope.RESOURCE) {
|
||||
return this.wrapError(ConfigurationEditingErrorCode.ERROR_INVALID_FOLDER_CONFIGURATION, target, operation);
|
||||
return this.reject(ConfigurationEditingErrorCode.ERROR_INVALID_FOLDER_CONFIGURATION, target, operation);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -425,12 +425,12 @@ export class ConfigurationEditingService {
|
||||
const model = reference.object.textEditorModel;
|
||||
|
||||
if (this.hasParseErrors(model, operation)) {
|
||||
return this.wrapError<typeof reference>(ConfigurationEditingErrorCode.ERROR_INVALID_CONFIGURATION, target, operation);
|
||||
return this.reject<typeof reference>(ConfigurationEditingErrorCode.ERROR_INVALID_CONFIGURATION, target, operation);
|
||||
}
|
||||
|
||||
// Target cannot be dirty if not writing into buffer
|
||||
if (checkDirty && this.textFileService.isDirty(operation.resource)) {
|
||||
return this.wrapError<typeof reference>(ConfigurationEditingErrorCode.ERROR_CONFIGURATION_FILE_DIRTY, target, operation);
|
||||
return this.reject<typeof reference>(ConfigurationEditingErrorCode.ERROR_CONFIGURATION_FILE_DIRTY, target, operation);
|
||||
}
|
||||
return reference;
|
||||
});
|
||||
@@ -441,8 +441,7 @@ export class ConfigurationEditingService {
|
||||
// Check for standalone workspace configurations
|
||||
if (config.key) {
|
||||
const standaloneConfigurationKeys = Object.keys(WORKSPACE_STANDALONE_CONFIGURATIONS);
|
||||
for (let i = 0; i < standaloneConfigurationKeys.length; i++) {
|
||||
const key = standaloneConfigurationKeys[i];
|
||||
for (const key of standaloneConfigurationKeys) {
|
||||
const resource = this.getConfigurationFileResource(target, WORKSPACE_STANDALONE_CONFIGURATIONS[key], overrides.resource);
|
||||
|
||||
// Check for prefix
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { dirname } from 'path';
|
||||
import * as assert from 'vs/base/common/assert';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { ResourceMap } from 'vs/base/common/map';
|
||||
@@ -36,7 +35,7 @@ import { massageFolderPathForWorkspace } from 'vs/platform/workspaces/node/works
|
||||
import { UserConfiguration } from 'vs/platform/configuration/node/configuration';
|
||||
import { IJSONSchema, IJSONSchemaMap } from 'vs/base/common/jsonSchema';
|
||||
import { localize } from 'vs/nls';
|
||||
import { isEqual } from 'vs/base/common/resources';
|
||||
import { isEqual, dirname } from 'vs/base/common/resources';
|
||||
import { mark } from 'vs/base/common/performance';
|
||||
|
||||
export class WorkspaceService extends Disposable implements IWorkspaceConfigurationService, IWorkspaceContextService {
|
||||
@@ -44,6 +43,8 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat
|
||||
public _serviceBrand: any;
|
||||
|
||||
private workspace: Workspace;
|
||||
private resolvePromise: Promise<void>;
|
||||
private resolveCallback: () => void;
|
||||
private _configuration: Configuration;
|
||||
private defaultConfiguration: DefaultConfigurationModel;
|
||||
private userConfiguration: UserConfiguration;
|
||||
@@ -52,7 +53,7 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat
|
||||
|
||||
private workspaceEditingQueue: Queue<void>;
|
||||
|
||||
protected readonly _onDidChangeConfiguration: Emitter<IConfigurationChangeEvent> = this._register(new Emitter<IConfigurationChangeEvent>({ leakWarningThreshold: 500 }));
|
||||
protected readonly _onDidChangeConfiguration: Emitter<IConfigurationChangeEvent> = this._register(new Emitter<IConfigurationChangeEvent>());
|
||||
public readonly onDidChangeConfiguration: Event<IConfigurationChangeEvent> = this._onDidChangeConfiguration.event;
|
||||
|
||||
protected readonly _onDidChangeWorkspaceFolders: Emitter<IWorkspaceFoldersChangeEvent> = this._register(new Emitter<IWorkspaceFoldersChangeEvent>());
|
||||
@@ -71,20 +72,25 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat
|
||||
constructor(private environmentService: IEnvironmentService, private workspaceSettingsRootFolder: string = FOLDER_CONFIG_FOLDER_NAME) {
|
||||
super();
|
||||
|
||||
this.resolvePromise = new Promise(c => this.resolveCallback = c);
|
||||
this.defaultConfiguration = new DefaultConfigurationModel();
|
||||
this.userConfiguration = this._register(new UserConfiguration(environmentService.appSettingsPath));
|
||||
this.workspaceConfiguration = this._register(new WorkspaceConfiguration());
|
||||
this.workspaceConfiguration = this._register(new WorkspaceConfiguration(environmentService));
|
||||
this._register(this.userConfiguration.onDidChangeConfiguration(userConfiguration => this.onUserConfigurationChanged(userConfiguration)));
|
||||
this._register(this.workspaceConfiguration.onDidUpdateConfiguration(() => this.onWorkspaceConfigurationChanged()));
|
||||
|
||||
this._register(Registry.as<IConfigurationRegistry>(Extensions.Configuration).onDidSchemaChange(e => this.registerConfigurationSchemas()));
|
||||
this._register(Registry.as<IConfigurationRegistry>(Extensions.Configuration).onDidRegisterConfiguration(configurationProperties => this.onDefaultConfigurationChanged(configurationProperties)));
|
||||
this._register(Registry.as<IConfigurationRegistry>(Extensions.Configuration).onDidUpdateConfiguration(configurationProperties => this.onDefaultConfigurationChanged(configurationProperties)));
|
||||
|
||||
this.workspaceEditingQueue = new Queue<void>();
|
||||
}
|
||||
|
||||
// Workspace Context Service Impl
|
||||
|
||||
public getCompleteWorkspace(): Promise<Workspace> {
|
||||
return this.resolvePromise.then(() => this.getWorkspace());
|
||||
}
|
||||
|
||||
public getWorkspace(): Workspace {
|
||||
return this.workspace;
|
||||
}
|
||||
@@ -137,11 +143,11 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat
|
||||
|
||||
private doUpdateFolders(foldersToAdd: IWorkspaceFolderCreationData[], foldersToRemove: URI[], index?: number): Promise<void> {
|
||||
if (this.getWorkbenchState() !== WorkbenchState.WORKSPACE) {
|
||||
return Promise.resolve(void 0); // we need a workspace to begin with
|
||||
return Promise.resolve(undefined); // we need a workspace to begin with
|
||||
}
|
||||
|
||||
if (foldersToAdd.length + foldersToRemove.length === 0) {
|
||||
return Promise.resolve(void 0); // nothing to do
|
||||
return Promise.resolve(undefined); // nothing to do
|
||||
}
|
||||
|
||||
let foldersHaveChanged = false;
|
||||
@@ -162,8 +168,8 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat
|
||||
if (foldersToAdd.length) {
|
||||
|
||||
// Recompute current workspace folders if we have folders to add
|
||||
const workspaceConfigFolder = dirname(this.getWorkspace().configuration.fsPath);
|
||||
currentWorkspaceFolders = toWorkspaceFolders(newStoredFolders, URI.file(workspaceConfigFolder));
|
||||
const workspaceConfigFolder = dirname(this.getWorkspace().configuration);
|
||||
currentWorkspaceFolders = toWorkspaceFolders(newStoredFolders, workspaceConfigFolder);
|
||||
const currentWorkspaceFolderUris = currentWorkspaceFolders.map(folder => folder.uri);
|
||||
|
||||
const storedFoldersToAdd: IStoredWorkspaceFolder[] = [];
|
||||
@@ -214,7 +220,7 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat
|
||||
return this.setFolders(newStoredFolders);
|
||||
}
|
||||
|
||||
return Promise.resolve(void 0);
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
private setFolders(folders: IStoredWorkspaceFolder[]): Promise<void> {
|
||||
@@ -245,8 +251,8 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat
|
||||
getValue<T>(overrides: IConfigurationOverrides): T;
|
||||
getValue<T>(section: string, overrides: IConfigurationOverrides): T;
|
||||
getValue(arg1?: any, arg2?: any): any {
|
||||
const section = typeof arg1 === 'string' ? arg1 : void 0;
|
||||
const overrides = isConfigurationOverrides(arg1) ? arg1 : isConfigurationOverrides(arg2) ? arg2 : void 0;
|
||||
const section = typeof arg1 === 'string' ? arg1 : undefined;
|
||||
const overrides = isConfigurationOverrides(arg1) ? arg1 : isConfigurationOverrides(arg2) ? arg2 : undefined;
|
||||
return this._configuration.getValue(section, overrides);
|
||||
}
|
||||
|
||||
@@ -257,7 +263,7 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat
|
||||
updateValue(key: string, value: any, overrides: IConfigurationOverrides, target: ConfigurationTarget, donotNotifyError: boolean): Promise<void>;
|
||||
updateValue(key: string, value: any, arg3?: any, arg4?: any, donotNotifyError?: any): Promise<void> {
|
||||
assert.ok(this.configurationEditingService, 'Workbench is not initialized yet');
|
||||
const overrides = isConfigurationOverrides(arg3) ? arg3 : void 0;
|
||||
const overrides = isConfigurationOverrides(arg3) ? arg3 : undefined;
|
||||
const target = this.deriveConfigurationTarget(key, value, overrides, overrides ? arg4 : arg3);
|
||||
return target ? this.writeConfigurationValue(key, value, target, overrides, donotNotifyError)
|
||||
: Promise.resolve(null);
|
||||
@@ -303,17 +309,22 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat
|
||||
acquireFileService(fileService: IFileService): void {
|
||||
this.fileService = fileService;
|
||||
const changedWorkspaceFolders: IWorkspaceFolder[] = [];
|
||||
Promise.all(this.cachedFolderConfigs.values()
|
||||
Promise.all([this.workspaceConfiguration.adopt(fileService), ...this.cachedFolderConfigs.values()
|
||||
.map(folderConfiguration => folderConfiguration.adopt(fileService)
|
||||
.then(result => {
|
||||
if (result) {
|
||||
changedWorkspaceFolders.push(folderConfiguration.workspaceFolder);
|
||||
}
|
||||
})))
|
||||
.then(() => {
|
||||
return result;
|
||||
}))])
|
||||
.then(([workspaceChanged]) => {
|
||||
if (workspaceChanged) {
|
||||
this.onWorkspaceConfigurationChanged();
|
||||
}
|
||||
for (const workspaceFolder of changedWorkspaceFolders) {
|
||||
this.onWorkspaceFolderConfigurationChanged(workspaceFolder);
|
||||
}
|
||||
this.resolveCallback();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -335,10 +346,10 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat
|
||||
}
|
||||
|
||||
private createMultiFolderWorkspace(workspaceIdentifier: IWorkspaceIdentifier): Promise<Workspace> {
|
||||
const workspaceConfigPath = URI.file(workspaceIdentifier.configPath);
|
||||
return this.workspaceConfiguration.load(workspaceConfigPath)
|
||||
return this.workspaceConfiguration.load({ id: workspaceIdentifier.id, configPath: URI.file(workspaceIdentifier.configPath) })
|
||||
.then(() => {
|
||||
const workspaceFolders = toWorkspaceFolders(this.workspaceConfiguration.getFolders(), URI.file(dirname(workspaceConfigPath.fsPath)));
|
||||
const workspaceConfigPath = URI.file(workspaceIdentifier.configPath);
|
||||
const workspaceFolders = toWorkspaceFolders(this.workspaceConfiguration.getFolders(), dirname(workspaceConfigPath));
|
||||
const workspaceId = workspaceIdentifier.id;
|
||||
return new Workspace(workspaceId, workspaceFolders, workspaceConfigPath);
|
||||
});
|
||||
@@ -369,7 +380,7 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat
|
||||
|
||||
if (hasWorkspaceBefore) {
|
||||
previousState = this.getWorkbenchState();
|
||||
previousWorkspacePath = this.workspace.configuration ? this.workspace.configuration.fsPath : void 0;
|
||||
previousWorkspacePath = this.workspace.configuration ? this.workspace.configuration.fsPath : undefined;
|
||||
previousFolders = this.workspace.folders;
|
||||
this.workspace.update(workspace);
|
||||
} else {
|
||||
@@ -387,7 +398,7 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat
|
||||
this._onDidChangeWorkbenchState.fire(newState);
|
||||
}
|
||||
|
||||
const newWorkspacePath = this.workspace.configuration ? this.workspace.configuration.fsPath : void 0;
|
||||
const newWorkspacePath = this.workspace.configuration ? this.workspace.configuration.fsPath : undefined;
|
||||
if (previousWorkspacePath && newWorkspacePath !== previousWorkspacePath || newState !== previousState) {
|
||||
this._onDidChangeWorkspaceName.fire();
|
||||
}
|
||||
@@ -436,7 +447,7 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat
|
||||
if (workbenchState === WorkbenchState.WORKSPACE) {
|
||||
return this.workspaceConfiguration.reload().then(() => this.onWorkspaceConfigurationChanged());
|
||||
}
|
||||
return Promise.resolve(null);
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
private reloadWorkspaceFolderConfiguration(folder: IWorkspaceFolder, key?: string): Promise<void> {
|
||||
@@ -531,7 +542,7 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat
|
||||
private onWorkspaceConfigurationChanged(): Promise<void> {
|
||||
if (this.workspace && this.workspace.configuration && this._configuration) {
|
||||
const workspaceConfigurationChangeEvent = this._configuration.compareAndUpdateWorkspaceConfiguration(this.workspaceConfiguration.getConfiguration());
|
||||
let configuredFolders = toWorkspaceFolders(this.workspaceConfiguration.getFolders(), URI.file(dirname(this.workspace.configuration.fsPath)));
|
||||
let configuredFolders = toWorkspaceFolders(this.workspaceConfiguration.getFolders(), dirname(this.workspace.configuration));
|
||||
const changes = this.compareFolders(this.workspace.folders, configuredFolders);
|
||||
if (changes.added.length || changes.removed.length || changes.changed.length) {
|
||||
this.workspace.folders = configuredFolders;
|
||||
@@ -544,7 +555,7 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat
|
||||
this.triggerConfigurationChange(workspaceConfigurationChangeEvent, ConfigurationTarget.WORKSPACE);
|
||||
}
|
||||
}
|
||||
return Promise.resolve(null);
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
private onWorkspaceFolderConfigurationChanged(folder: IWorkspaceFolder, key?: string): Promise<void> {
|
||||
@@ -606,7 +617,7 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat
|
||||
if (target === ConfigurationTarget.MEMORY) {
|
||||
this._configuration.updateValue(key, value, overrides);
|
||||
this.triggerConfigurationChange(new ConfigurationChangeEvent().change(overrides.overrideIdentifier ? [keyFromOverrideIdentifier(overrides.overrideIdentifier)] : [key], overrides.resource), target);
|
||||
return Promise.resolve(null);
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
return this.configurationEditingService.writeConfiguration(target, { key, value }, { scopes: overrides, donotNotifyError })
|
||||
@@ -631,22 +642,22 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat
|
||||
return target;
|
||||
}
|
||||
|
||||
if (value === void 0) {
|
||||
if (value === undefined) {
|
||||
// Ignore. But expected is to remove the value from all targets
|
||||
return void 0;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const inspect = this.inspect(key, overrides);
|
||||
if (equals(value, inspect.value)) {
|
||||
// No change. So ignore.
|
||||
return void 0;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (inspect.workspaceFolder !== void 0) {
|
||||
if (inspect.workspaceFolder !== undefined) {
|
||||
return ConfigurationTarget.WORKSPACE_FOLDER;
|
||||
}
|
||||
|
||||
if (inspect.workspace !== void 0) {
|
||||
if (inspect.workspace !== undefined) {
|
||||
return ConfigurationTarget.WORKSPACE;
|
||||
}
|
||||
|
||||
@@ -693,8 +704,8 @@ export class DefaultConfigurationExportHelper {
|
||||
|
||||
constructor(
|
||||
@IEnvironmentService environmentService: IEnvironmentService,
|
||||
@IExtensionService private extensionService: IExtensionService,
|
||||
@ICommandService private commandService: ICommandService) {
|
||||
@IExtensionService private readonly extensionService: IExtensionService,
|
||||
@ICommandService private readonly commandService: ICommandService) {
|
||||
if (environmentService.args['export-default-configuration']) {
|
||||
this.writeConfigModelAndQuit(environmentService.args['export-default-configuration']);
|
||||
}
|
||||
|
||||
@@ -28,9 +28,9 @@ export class JSONEditingService implements IJSONEditingService {
|
||||
private queue: Queue<void>;
|
||||
|
||||
constructor(
|
||||
@IFileService private fileService: IFileService,
|
||||
@ITextModelService private textModelResolverService: ITextModelService,
|
||||
@ITextFileService private textFileService: ITextFileService
|
||||
@IFileService private readonly fileService: IFileService,
|
||||
@ITextModelService private readonly textModelResolverService: ITextModelService,
|
||||
@ITextFileService private readonly textFileService: ITextFileService
|
||||
) {
|
||||
this.queue = new Queue<void>();
|
||||
}
|
||||
@@ -103,18 +103,18 @@ export class JSONEditingService implements IJSONEditingService {
|
||||
const model = reference.object.textEditorModel;
|
||||
|
||||
if (this.hasParseErrors(model)) {
|
||||
return this.wrapError<IReference<ITextEditorModel>>(JSONEditingErrorCode.ERROR_INVALID_FILE);
|
||||
return this.reject<IReference<ITextEditorModel>>(JSONEditingErrorCode.ERROR_INVALID_FILE);
|
||||
}
|
||||
|
||||
// Target cannot be dirty if not writing into buffer
|
||||
if (checkDirty && this.textFileService.isDirty(resource)) {
|
||||
return this.wrapError<IReference<ITextEditorModel>>(JSONEditingErrorCode.ERROR_FILE_DIRTY);
|
||||
return this.reject<IReference<ITextEditorModel>>(JSONEditingErrorCode.ERROR_FILE_DIRTY);
|
||||
}
|
||||
return reference;
|
||||
});
|
||||
}
|
||||
|
||||
private wrapError<T>(code: JSONEditingErrorCode): Promise<T> {
|
||||
private reject<T>(code: JSONEditingErrorCode): Promise<T> {
|
||||
const message = this.toErrorMessage(code);
|
||||
return Promise.reject(new JSONEditingError(message, code));
|
||||
}
|
||||
|
||||
@@ -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 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']);
|
||||
|
||||
@@ -123,18 +123,18 @@ suite('ConfigurationEditingService', () => {
|
||||
if (configuraitonService) {
|
||||
configuraitonService.dispose();
|
||||
}
|
||||
instantiationService = null;
|
||||
instantiationService = null!;
|
||||
}
|
||||
}
|
||||
|
||||
function clearWorkspace(): Promise<void> {
|
||||
return new Promise<void>((c, e) => {
|
||||
if (parentDir) {
|
||||
extfs.del(parentDir, os.tmpdir(), () => c(null), () => c(null));
|
||||
extfs.del(parentDir, os.tmpdir(), () => c(undefined), () => c(undefined));
|
||||
} else {
|
||||
c(null);
|
||||
c(undefined);
|
||||
}
|
||||
}).then(() => parentDir = null);
|
||||
}).then(() => parentDir = null!);
|
||||
}
|
||||
|
||||
test('errors cases - invalid key', () => {
|
||||
@@ -179,7 +179,7 @@ suite('ConfigurationEditingService', () => {
|
||||
test('do not notify error', () => {
|
||||
instantiationService.stub(ITextFileService, 'isDirty', true);
|
||||
const target = sinon.stub();
|
||||
instantiationService.stub(INotificationService, <INotificationService>{ prompt: target, _serviceBrand: null, notify: null, error: null, info: null, warn: null });
|
||||
instantiationService.stub(INotificationService, <INotificationService>{ prompt: target, _serviceBrand: null, notify: null!, error: null!, info: null!, warn: null! });
|
||||
return testObject.writeConfiguration(ConfigurationTarget.USER, { key: 'configurationEditing.service.testSetting', value: 'value' }, { donotNotifyError: true })
|
||||
.then(() => assert.fail('Should fail with ERROR_CONFIGURATION_FILE_DIRTY error.'),
|
||||
(error: ConfigurationEditingError) => {
|
||||
|
||||
@@ -19,7 +19,7 @@ import { IConfigurationRegistry, Extensions as ConfigurationExtensions, Configur
|
||||
import { WorkspaceService } from 'vs/workbench/services/configuration/node/configurationService';
|
||||
import { ISingleFolderWorkspaceInitializationPayload } from 'vs/platform/workspaces/common/workspaces';
|
||||
import { ConfigurationEditingErrorCode } from 'vs/workbench/services/configuration/node/configurationEditingService';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { IFileService, FileChangesEvent, FileChangeType } from 'vs/platform/files/common/files';
|
||||
import { IWorkspaceContextService, WorkbenchState, IWorkspaceFoldersChangeEvent } from 'vs/platform/workspace/common/workspace';
|
||||
import { ConfigurationTarget, IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration';
|
||||
import { workbenchInstantiationService, TestTextResourceConfigurationService, TestTextFileService, TestLifecycleService, TestEnvironmentService, TestStorageService } from 'vs/workbench/test/workbenchTestServices';
|
||||
@@ -34,6 +34,7 @@ import { JSONEditingService } from 'vs/workbench/services/configuration/node/jso
|
||||
import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration';
|
||||
import { Uri } from 'vscode';
|
||||
import { createHash } from 'crypto';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
|
||||
class SettingsTestEnvironmentService extends EnvironmentService {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user