Merge VS Code 1.31.1 (#4283)

This commit is contained in:
Matt Irvine
2019-03-15 13:09:45 -07:00
committed by GitHub
parent 7d31575149
commit 86bac90001
1716 changed files with 53308 additions and 48375 deletions

View File

@@ -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")
});

View File

@@ -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);
}

View File

@@ -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);
}
}

View File

@@ -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

View File

@@ -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']);
}

View File

@@ -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));
}

View File

@@ -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']);

View File

@@ -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) => {

View File

@@ -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 {