Merge VS Code 1.23.1 (#1520)

This commit is contained in:
Matt Irvine
2018-06-05 11:24:51 -07:00
committed by GitHub
parent e3baf5c443
commit 0c58f09e59
3651 changed files with 74249 additions and 48599 deletions

View File

@@ -14,10 +14,6 @@ export const FOLDER_SETTINGS_PATH = `${FOLDER_CONFIG_FOLDER_NAME}/${FOLDER_SETTI
export const IWorkspaceConfigurationService = createDecorator<IWorkspaceConfigurationService>('configurationService');
export interface IWorkspaceConfigurationService extends IConfigurationService {
/**
* Returns untrusted configuration keys for the current workspace.
*/
getUnsupportedWorkspaceKeys(): string[];
}
export const defaultSettingsSchemaId = 'vscode://schemas/settings/default';
@@ -30,5 +26,5 @@ export const TASKS_CONFIGURATION_KEY = 'tasks';
export const LAUNCH_CONFIGURATION_KEY = 'launch';
export const WORKSPACE_STANDALONE_CONFIGURATIONS = Object.create(null);
WORKSPACE_STANDALONE_CONFIGURATIONS[TASKS_CONFIGURATION_KEY] = `${FOLDER_CONFIG_FOLDER_NAME}/tasks.json`;
WORKSPACE_STANDALONE_CONFIGURATIONS[LAUNCH_CONFIGURATION_KEY] = `${FOLDER_CONFIG_FOLDER_NAME}/launch.json`;
WORKSPACE_STANDALONE_CONFIGURATIONS[TASKS_CONFIGURATION_KEY] = `${FOLDER_CONFIG_FOLDER_NAME}/${TASKS_CONFIGURATION_KEY}.json`;
WORKSPACE_STANDALONE_CONFIGURATIONS[LAUNCH_CONFIGURATION_KEY] = `${FOLDER_CONFIG_FOLDER_NAME}/${LAUNCH_CONFIGURATION_KEY}.json`;

View File

@@ -32,13 +32,15 @@ const configurationEntrySchema: IJSONSchema = {
type: 'object',
properties: {
isExecutable: {
type: 'boolean'
type: 'boolean',
deprecationMessage: 'This property is deprecated. Instead use `scope` property and set it to `application` value.'
},
scope: {
type: 'string',
enum: ['window', 'resource'],
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.")
],
@@ -130,7 +132,17 @@ function validateProperties(configuration: IConfigurationNode, extension: IExten
for (let key in properties) {
const message = validateProperty(key);
const propertyConfiguration = configuration.properties[key];
propertyConfiguration.scope = propertyConfiguration.scope && propertyConfiguration.scope.toString() === 'resource' ? ConfigurationScope.RESOURCE : ConfigurationScope.WINDOW;
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;
}
propertyConfiguration.notMultiRootAdopted = !(extension.description.isBuiltin || (Array.isArray(extension.description.keywords) && extension.description.keywords.indexOf('multi-root ready') !== -1));
if (message) {
extension.collector.warn(message);

View File

@@ -5,30 +5,15 @@
'use strict';
import { equals } from 'vs/base/common/objects';
import { compare, toValuesTree, IConfigurationChangeEvent, ConfigurationTarget, IConfigurationModel, IConfigurationOverrides, IOverrides } from 'vs/platform/configuration/common/configuration';
import { compare, toValuesTree, IConfigurationChangeEvent, ConfigurationTarget, IConfigurationModel, IConfigurationOverrides } from 'vs/platform/configuration/common/configuration';
import { Configuration as BaseConfiguration, ConfigurationModelParser, ConfigurationChangeEvent, ConfigurationModel, AbstractConfigurationChangeEvent } from 'vs/platform/configuration/common/configurationModels';
import { Registry } from 'vs/platform/registry/common/platform';
import { IConfigurationRegistry, IConfigurationPropertySchema, Extensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry';
import { IStoredWorkspaceFolder } from 'vs/platform/workspaces/common/workspaces';
import { Workspace } from 'vs/platform/workspace/common/workspace';
import { StrictResourceMap } from 'vs/base/common/map';
import { ResourceMap } from 'vs/base/common/map';
import URI from 'vs/base/common/uri';
export class SettingsModel extends ConfigurationModel {
private _unsupportedKeys: string[];
constructor(contents: any, keys: string[], overrides: IOverrides[], unsupportedKeys: string[]) {
super(contents, keys, overrides);
this._unsupportedKeys = unsupportedKeys;
}
public get unsupportedKeys(): string[] {
return this._unsupportedKeys;
}
}
export class WorkspaceConfigurationModelParser extends ConfigurationModelParser {
private _folders: IStoredWorkspaceFolder[] = [];
@@ -37,7 +22,7 @@ export class WorkspaceConfigurationModelParser extends ConfigurationModelParser
constructor(name: string) {
super(name);
this._settingsModelParser = new FolderSettingsModelParser(name);
this._settingsModelParser = new FolderSettingsModelParser(name, [ConfigurationScope.WINDOW, ConfigurationScope.RESOURCE]);
this._launchModel = new ConfigurationModel();
}
@@ -45,8 +30,8 @@ export class WorkspaceConfigurationModelParser extends ConfigurationModelParser
return this._folders;
}
get settingsModel(): SettingsModel {
return this._settingsModelParser.settingsModel;
get settingsModel(): ConfigurationModel {
return this._settingsModelParser.configurationModel;
}
get launchModel(): ConfigurationModel {
@@ -96,9 +81,9 @@ export class StandaloneConfigurationModelParser extends ConfigurationModelParser
export class FolderSettingsModelParser extends ConfigurationModelParser {
private _raw: any;
private _settingsModel: SettingsModel;
private _settingsModel: ConfigurationModel;
constructor(name: string, private configurationScope?: ConfigurationScope) {
constructor(name: string, private scopes: ConfigurationScope[]) {
super(name);
}
@@ -108,11 +93,7 @@ export class FolderSettingsModelParser extends ConfigurationModelParser {
}
get configurationModel(): ConfigurationModel {
return this._settingsModel || new SettingsModel({}, [], [], []);
}
get settingsModel(): SettingsModel {
return <SettingsModel>this.configurationModel;
return this._settingsModel || new ConfigurationModel();
}
reprocess(): void {
@@ -120,34 +101,22 @@ export class FolderSettingsModelParser extends ConfigurationModelParser {
}
private parseWorkspaceSettings(rawSettings: any): void {
const unsupportedKeys = [];
const rawWorkspaceSettings = {};
const configurationProperties = Registry.as<IConfigurationRegistry>(Extensions.Configuration).getConfigurationProperties();
for (let key in rawSettings) {
if (this.isNotExecutable(key, configurationProperties)) {
if (this.configurationScope === void 0 || this.getScope(key, configurationProperties) === this.configurationScope) {
rawWorkspaceSettings[key] = rawSettings[key];
}
} else {
unsupportedKeys.push(key);
const scope = this.getScope(key, configurationProperties);
if (this.scopes.indexOf(scope) !== -1) {
rawWorkspaceSettings[key] = rawSettings[key];
}
}
const configurationModel = this.parseRaw(rawWorkspaceSettings);
this._settingsModel = new SettingsModel(configurationModel.contents, configurationModel.keys, configurationModel.overrides, unsupportedKeys);
this._settingsModel = new ConfigurationModel(configurationModel.contents, configurationModel.keys, configurationModel.overrides);
}
private getScope(key: string, configurationProperties: { [qualifiedKey: string]: IConfigurationPropertySchema }): ConfigurationScope {
const propertySchema = configurationProperties[key];
return propertySchema ? propertySchema.scope : ConfigurationScope.WINDOW;
}
private isNotExecutable(key: string, configurationProperties: { [qualifiedKey: string]: IConfigurationPropertySchema }): boolean {
const propertySchema = configurationProperties[key];
if (!propertySchema) {
return true; // Unknown propertis are ignored from checks
}
return !propertySchema.isExecutable;
}
}
export class Configuration extends BaseConfiguration {
@@ -156,9 +125,9 @@ export class Configuration extends BaseConfiguration {
defaults: ConfigurationModel,
user: ConfigurationModel,
workspaceConfiguration: ConfigurationModel,
folders: StrictResourceMap<ConfigurationModel>,
folders: ResourceMap<ConfigurationModel>,
memoryConfiguration: ConfigurationModel,
memoryConfigurationByResource: StrictResourceMap<ConfigurationModel>,
memoryConfigurationByResource: ResourceMap<ConfigurationModel>,
private readonly _workspace: Workspace) {
super(defaults, user, workspaceConfiguration, folders, memoryConfiguration, memoryConfigurationByResource);
}
@@ -266,8 +235,8 @@ export class AllKeysConfigurationChangeEvent extends AbstractConfigurationChange
return this._changedConfiguration;
}
get changedConfigurationByResource(): StrictResourceMap<IConfigurationModel> {
return new StrictResourceMap();
get changedConfigurationByResource(): ResourceMap<IConfigurationModel> {
return new ResourceMap();
}
get affectedKeys(): string[] {
@@ -287,7 +256,7 @@ export class WorkspaceConfigurationChangeEvent implements IConfigurationChangeEv
return this.configurationChangeEvent.changedConfiguration;
}
get changedConfigurationByResource(): StrictResourceMap<IConfigurationModel> {
get changedConfigurationByResource(): ResourceMap<IConfigurationModel> {
return this.configurationChangeEvent.changedConfigurationByResource;
}
@@ -317,4 +286,4 @@ export class WorkspaceConfigurationChangeEvent implements IConfigurationChangeEv
return false;
}
}
}

View File

@@ -4,74 +4,31 @@
*--------------------------------------------------------------------------------------------*/
import URI from 'vs/base/common/uri';
import { createHash } from 'crypto';
import * as paths from 'vs/base/common/paths';
import { TPromise } from 'vs/base/common/winjs.base';
import Event, { Emitter } from 'vs/base/common/event';
import { readFile } from 'vs/base/node/pfs';
import { Event, Emitter } from 'vs/base/common/event';
import * as pfs from 'vs/base/node/pfs';
import * as errors from 'vs/base/common/errors';
import * as collections from 'vs/base/common/collections';
import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle';
import { RunOnceScheduler } from 'vs/base/common/async';
import { FileChangeType, FileChangesEvent } from 'vs/platform/files/common/files';
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, ConfigurationModelParser } from 'vs/platform/configuration/common/configurationModels';
import { ConfigurationModel } from 'vs/platform/configuration/common/configurationModels';
import { WorkspaceConfigurationModelParser, FolderSettingsModelParser, StandaloneConfigurationModelParser } from 'vs/workbench/services/configuration/common/configurationModels';
import { WORKSPACE_STANDALONE_CONFIGURATIONS, FOLDER_SETTINGS_PATH, TASKS_CONFIGURATION_KEY, LAUNCH_CONFIGURATION_KEY } from 'vs/workbench/services/configuration/common/configuration';
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 * as extfs from 'vs/base/node/extfs';
import { JSONEditingService } from 'vs/workbench/services/configuration/node/jsonEditingService';
import { WorkbenchState } from 'vs/platform/workspace/common/workspace';
import { WorkbenchState, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry';
import { relative } from 'path';
import { equals } from 'vs/base/common/objects';
// node.hs helper functions
interface IStat {
resource: URI;
isDirectory?: boolean;
children?: { resource: URI; }[];
}
interface IContent {
resource: URI;
value: string;
}
function resolveContents(resources: URI[]): TPromise<IContent[]> {
const contents: IContent[] = [];
return TPromise.join(resources.map(resource => {
return resolveContent(resource).then(content => {
contents.push(content);
});
})).then(() => contents);
}
function resolveContent(resource: URI): TPromise<IContent> {
return readFile(resource.fsPath).then(contents => ({ resource, value: contents.toString() }));
}
function resolveStat(resource: URI): TPromise<IStat> {
return new TPromise<IStat>((c, e) => {
extfs.readdir(resource.fsPath, (error, children) => {
if (error) {
if ((<any>error).code === 'ENOTDIR') {
c({ resource });
} else {
e(error);
}
} else {
c({
resource,
isDirectory: true,
children: children.map(child => { return { resource: URI.file(paths.join(resource.fsPath, child)) }; })
});
}
});
});
}
import { Schemas } from 'vs/base/common/network';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IConfigurationModel } from 'vs/platform/configuration/common/configuration';
export class WorkspaceConfiguration extends Disposable {
@@ -79,7 +36,7 @@ export class WorkspaceConfiguration extends Disposable {
private _workspaceConfigurationWatcher: ConfigWatcher<WorkspaceConfigurationModelParser>;
private _workspaceConfigurationWatcherDisposables: IDisposable[] = [];
private _onDidUpdateConfiguration: Emitter<void> = this._register(new Emitter<void>());
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 : '');
@@ -135,10 +92,6 @@ export class WorkspaceConfiguration extends Disposable {
return this._cache;
}
getUnsupportedKeys(): string[] {
return this._workspaceConfigurationModelParser.settingsModel.unsupportedKeys;
}
reprocessWorkspaceSettings(): ConfigurationModel {
this._workspaceConfigurationModelParser.reprocessWorkspaceSettings();
this.consolidate();
@@ -170,108 +123,201 @@ export class WorkspaceConfiguration extends Disposable {
}
}
export class FolderConfiguration extends Disposable {
function isFolderConfigurationFile(resource: URI): boolean {
const name = paths.basename(resource.path);
return [`${FOLDER_SETTINGS_NAME}.json`, `${TASKS_CONFIGURATION_KEY}.json`, `${LAUNCH_CONFIGURATION_KEY}.json`].some(p => p === name);// only workspace config files
}
private static readonly RELOAD_CONFIGURATION_DELAY = 50;
export interface IFolderConfiguration {
readonly onDidChange: Event<void>;
readonly loaded: boolean;
loadConfiguration(): TPromise<ConfigurationModel>;
reprocess(): ConfigurationModel;
dispose(): void;
}
private bulkFetchFromWorkspacePromise: TPromise;
private workspaceFilePathToConfiguration: { [relativeWorkspacePath: string]: TPromise<ConfigurationModelParser> };
export abstract class AbstractFolderConfiguration extends Disposable implements IFolderConfiguration {
private _folderSettingsModelParser: FolderSettingsModelParser;
private _standAloneConfigurations: ConfigurationModel[] = [];
private _cache: ConfigurationModel = new ConfigurationModel();
private _standAloneConfigurations: ConfigurationModel[];
private _cache: ConfigurationModel;
private _loaded: boolean = false;
private reloadConfigurationScheduler: RunOnceScheduler;
private reloadConfigurationEventEmitter: Emitter<ConfigurationModel> = new Emitter<ConfigurationModel>();
protected readonly _onDidChange: Emitter<void> = this._register(new Emitter<void>());
readonly onDidChange: Event<void> = this._onDidChange.event;
constructor(private folder: URI, private configFolderRelativePath: string, workbenchState: WorkbenchState) {
constructor(protected readonly folder: URI, workbenchState: WorkbenchState, from?: AbstractFolderConfiguration) {
super();
this._folderSettingsModelParser = new FolderSettingsModelParser(FOLDER_SETTINGS_PATH, WorkbenchState.WORKSPACE === workbenchState ? ConfigurationScope.RESOURCE : void 0);
this.workspaceFilePathToConfiguration = Object.create(null);
this.reloadConfigurationScheduler = this._register(new RunOnceScheduler(() => this.loadConfiguration().then(configuration => this.reloadConfigurationEventEmitter.fire(configuration), errors.onUnexpectedError), FolderConfiguration.RELOAD_CONFIGURATION_DELAY));
this._folderSettingsModelParser = from ? from._folderSettingsModelParser : new FolderSettingsModelParser(FOLDER_SETTINGS_PATH, WorkbenchState.WORKSPACE === workbenchState ? [ConfigurationScope.RESOURCE] : [ConfigurationScope.WINDOW, ConfigurationScope.RESOURCE]);
this._standAloneConfigurations = from ? from._standAloneConfigurations : [];
this._cache = from ? from._cache : new ConfigurationModel();
}
get loaded(): boolean {
return this._loaded;
}
loadConfiguration(): TPromise<ConfigurationModel> {
// Load workspace locals
return this.loadWorkspaceConfigFiles().then(workspaceConfigFiles => {
this._standAloneConfigurations = Object.keys(workspaceConfigFiles).filter(key => key !== FOLDER_SETTINGS_PATH).map(key => <ConfigurationModel>workspaceConfigFiles[key].configurationModel);
// Consolidate (support *.json files in the workspace settings folder)
this.consolidate();
return this._cache;
});
return this.loadFolderConfigurationContents()
.then((contents) => {
// reset
this._standAloneConfigurations = [];
this._folderSettingsModelParser.parse('');
// parse
this.parseContents(contents);
// Consolidate (support *.json files in the workspace settings folder)
this.consolidate();
this._loaded = true;
return this._cache;
});
}
reprocess(): ConfigurationModel {
const oldContents = this._folderSettingsModelParser.settingsModel.contents;
const oldContents = this._folderSettingsModelParser.configurationModel.contents;
this._folderSettingsModelParser.reprocess();
if (!equals(oldContents, this._folderSettingsModelParser.settingsModel.contents)) {
if (!equals(oldContents, this._folderSettingsModelParser.configurationModel.contents)) {
this.consolidate();
}
return this._cache;
}
getUnsupportedKeys(): string[] {
return this._folderSettingsModelParser.settingsModel.unsupportedKeys;
}
private consolidate(): void {
this._cache = this._folderSettingsModelParser.settingsModel.merge(...this._standAloneConfigurations);
this._cache = this._folderSettingsModelParser.configurationModel.merge(...this._standAloneConfigurations);
}
private loadWorkspaceConfigFiles(): TPromise<{ [relativeWorkspacePath: string]: ConfigurationModelParser }> {
// once: when invoked for the first time we fetch json files that contribute settings
if (!this.bulkFetchFromWorkspacePromise) {
this.bulkFetchFromWorkspacePromise = resolveStat(this.toResource(this.configFolderRelativePath)).then(stat => {
if (!stat.isDirectory) {
return TPromise.as([]);
private parseContents(contents: { resource: URI, value: string }[]): void {
for (const content of contents) {
const name = paths.basename(content.resource.path);
if (name === `${FOLDER_SETTINGS_NAME}.json`) {
this._folderSettingsModelParser.parse(content.value);
} else {
const matches = /([^\.]*)*\.json/.exec(name);
if (matches && matches[1]) {
const standAloneConfigurationModelParser = new StandaloneConfigurationModelParser(content.resource.toString(), matches[1]);
standAloneConfigurationModelParser.parse(content.value);
this._standAloneConfigurations.push(standAloneConfigurationModelParser.configurationModel);
}
}
}
}
return resolveContents(stat.children.filter(stat => {
const isJson = paths.extname(stat.resource.fsPath) === '.json';
if (!isJson) {
return false; // only JSON files
protected abstract loadFolderConfigurationContents(): TPromise<{ resource: URI, value: string }[]>;
}
export class NodeBasedFolderConfiguration extends AbstractFolderConfiguration {
private readonly folderConfigurationPath: URI;
constructor(folder: URI, configFolderRelativePath: string, workbenchState: WorkbenchState) {
super(folder, workbenchState);
this.folderConfigurationPath = URI.file(paths.join(this.folder.fsPath, configFolderRelativePath));
}
protected loadFolderConfigurationContents(): TPromise<{ resource: URI, value: string }[]> {
return this.resolveStat(this.folderConfigurationPath).then(stat => {
if (!stat.isDirectory) {
return TPromise.as([]);
}
return this.resolveContents(stat.children.filter(stat => isFolderConfigurationFile(stat.resource))
.map(stat => stat.resource));
}, err => [] /* never fail this call */)
.then(null, errors.onUnexpectedError);
}
private resolveContents(resources: URI[]): TPromise<{ resource: URI, value: string }[]> {
return TPromise.join(resources.map(resource =>
pfs.readFile(resource.fsPath)
.then(contents => ({ resource, value: contents.toString() }))));
}
private resolveStat(resource: URI): TPromise<{ resource: URI, isDirectory?: boolean, children?: { resource: URI; }[] }> {
return new TPromise<{ resource: URI, isDirectory?: boolean, children?: { resource: URI; }[] }>((c, e) => {
extfs.readdir(resource.fsPath, (error, children) => {
if (error) {
if ((<any>error).code === 'ENOTDIR') {
c({ resource });
} else {
e(error);
}
} else {
c({
resource,
isDirectory: true,
children: children.map(child => { return { resource: URI.file(paths.join(resource.fsPath, child)) }; })
});
}
});
});
}
}
return this.isWorkspaceConfigurationFile(this.toFolderRelativePath(stat.resource)); // only workspace config files
}).map(stat => stat.resource));
}, err => [] /* never fail this call */)
.then((contents: IContent[]) => {
contents.forEach(content => this.workspaceFilePathToConfiguration[this.toFolderRelativePath(content.resource)] = TPromise.as(this.createConfigurationModelParser(content)));
}, errors.onUnexpectedError);
export class FileServiceBasedFolderConfiguration extends AbstractFolderConfiguration {
private bulkContentFetchromise: TPromise<any>;
private workspaceFilePathToConfiguration: { [relativeWorkspacePath: string]: TPromise<IContent> };
private reloadConfigurationScheduler: RunOnceScheduler;
private readonly folderConfigurationPath: URI;
constructor(folder: URI, private configFolderRelativePath: string, workbenchState: WorkbenchState, private fileService: IFileService, from?: AbstractFolderConfiguration) {
super(folder, workbenchState, from);
this.folderConfigurationPath = folder.with({ path: paths.join(this.folder.path, configFolderRelativePath) });
this.workspaceFilePathToConfiguration = Object.create(null);
this.reloadConfigurationScheduler = this._register(new RunOnceScheduler(() => this._onDidChange.fire(), 50));
this._register(fileService.onFileChanges(e => this.handleWorkspaceFileEvents(e)));
}
protected loadFolderConfigurationContents(): TPromise<{ resource: URI, value: string }[]> {
// once: when invoked for the first time we fetch json files that contribute settings
if (!this.bulkContentFetchromise) {
this.bulkContentFetchromise = this.fileService.resolveFile(this.folderConfigurationPath)
.then(stat => {
if (stat.isDirectory && stat.children) {
stat.children
.filter(child => isFolderConfigurationFile(child.resource))
.forEach(child => this.workspaceFilePathToConfiguration[this.toFolderRelativePath(child.resource)] = this.fileService.resolveContent(child.resource).then(null, errors.onUnexpectedError));
}
}).then(null, err => [] /* never fail this call */);
}
// on change: join on *all* configuration file promises so that we can merge them into a single configuration object. this
// happens whenever a config file changes, is deleted, or added
return this.bulkFetchFromWorkspacePromise.then(() => TPromise.join(this.workspaceFilePathToConfiguration));
return this.bulkContentFetchromise.then(() => TPromise.join(this.workspaceFilePathToConfiguration).then(result => collections.values(result)));
}
public handleWorkspaceFileEvents(event: FileChangesEvent): TPromise<ConfigurationModel> {
private handleWorkspaceFileEvents(event: FileChangesEvent): void {
const events = event.changes;
let affectedByChanges = false;
// Find changes that affect workspace configuration files
for (let i = 0, len = events.length; i < len; i++) {
const resource = events[i].resource;
const isJson = paths.extname(resource.fsPath) === '.json';
const isDeletedSettingsFolder = (events[i].type === FileChangeType.DELETED && paths.isEqual(paths.basename(resource.fsPath), this.configFolderRelativePath));
const basename = paths.basename(resource.path);
const isJson = paths.extname(basename) === '.json';
const isDeletedSettingsFolder = (events[i].type === FileChangeType.DELETED && basename === this.configFolderRelativePath);
if (!isJson && !isDeletedSettingsFolder) {
continue; // only JSON files or the actual settings folder
}
const workspacePath = this.toFolderRelativePath(resource);
if (!workspacePath) {
continue; // event is not inside workspace
const folderRelativePath = this.toFolderRelativePath(resource);
if (!folderRelativePath) {
continue; // event is not inside folder
}
// Handle case where ".vscode" got deleted
if (workspacePath === this.configFolderRelativePath && events[i].type === FileChangeType.DELETED) {
if (isDeletedSettingsFolder) {
this.workspaceFilePathToConfiguration = Object.create(null);
affectedByChanges = true;
}
// only valid workspace config files
if (!this.isWorkspaceConfigurationFile(workspacePath)) {
if (!isFolderConfigurationFile(resource)) {
continue;
}
@@ -279,72 +325,176 @@ export class FolderConfiguration extends Disposable {
// remove promises for delete events
switch (events[i].type) {
case FileChangeType.DELETED:
affectedByChanges = collections.remove(this.workspaceFilePathToConfiguration, workspacePath);
affectedByChanges = collections.remove(this.workspaceFilePathToConfiguration, folderRelativePath);
break;
case FileChangeType.UPDATED:
case FileChangeType.ADDED:
this.workspaceFilePathToConfiguration[workspacePath] = resolveContent(resource).then(content => this.createConfigurationModelParser(content), errors.onUnexpectedError);
this.workspaceFilePathToConfiguration[folderRelativePath] = this.fileService.resolveContent(resource).then(null, errors.onUnexpectedError);
affectedByChanges = true;
}
}
if (!affectedByChanges) {
return TPromise.as(null);
if (affectedByChanges) {
this.reloadConfigurationScheduler.schedule();
}
}
return new TPromise((c, e) => {
let disposable = this.reloadConfigurationEventEmitter.event(configuration => {
disposable.dispose();
c(configuration);
});
// trigger reload of the configuration if we are affected by changes
if (!this.reloadConfigurationScheduler.isScheduled()) {
this.reloadConfigurationScheduler.schedule();
private toFolderRelativePath(resource: URI): string {
if (resource.scheme === Schemas.file) {
if (paths.isEqualOrParent(resource.fsPath, this.folderConfigurationPath.fsPath, !isLinux /* ignorecase */)) {
return paths.normalize(relative(this.folderConfigurationPath.fsPath, resource.fsPath));
}
} else {
if (paths.isEqualOrParent(resource.path, this.folderConfigurationPath.path, true /* ignorecase */)) {
return paths.normalize(relative(this.folderConfigurationPath.path, resource.path));
}
}
return null;
}
}
export class CachedFolderConfiguration extends Disposable implements IFolderConfiguration {
private readonly _onDidChange: Emitter<void> = this._register(new Emitter<void>());
readonly onDidChange: Event<void> = this._onDidChange.event;
private readonly cachedFolderPath: string;
private readonly cachedConfigurationPath: string;
private configurationModel: ConfigurationModel;
loaded: boolean = false;
constructor(
folder: URI,
configFolderRelativePath: string,
environmentService: IEnvironmentService) {
super();
this.cachedFolderPath = paths.join(environmentService.appSettingsHome, createHash('md5').update(paths.join(folder.path, configFolderRelativePath)).digest('hex'));
this.cachedConfigurationPath = paths.join(this.cachedFolderPath, 'configuration.json');
this.configurationModel = new ConfigurationModel();
}
loadConfiguration(): TPromise<ConfigurationModel> {
return pfs.readFile(this.cachedConfigurationPath)
.then(contents => {
const parsed: IConfigurationModel = JSON.parse(contents.toString());
this.configurationModel = new ConfigurationModel(parsed.contents, parsed.keys, parsed.overrides);
this.loaded = true;
return this.configurationModel;
}, () => this.configurationModel);
}
updateConfiguration(configurationModel: ConfigurationModel): TPromise<void> {
const raw = JSON.stringify(configurationModel.toJSON());
return this.createCachedFolder().then(created => {
if (created) {
return configurationModel.keys.length ? pfs.writeFile(this.cachedConfigurationPath, raw) : pfs.rimraf(this.cachedFolderPath);
}
return null;
});
}
private createConfigurationModelParser(content: IContent): ConfigurationModelParser {
const path = this.toFolderRelativePath(content.resource);
if (path === FOLDER_SETTINGS_PATH) {
this._folderSettingsModelParser.parse(content.value);
return this._folderSettingsModelParser;
} else {
const matches = /\/([^\.]*)*\.json/.exec(path);
if (matches && matches[1]) {
const standAloneConfigurationModelParser = new StandaloneConfigurationModelParser(content.resource.toString(), matches[1]);
standAloneConfigurationModelParser.parse(content.value);
return standAloneConfigurationModelParser;
reprocess(): ConfigurationModel {
return this.configurationModel;
}
getUnsupportedKeys(): string[] {
return [];
}
private createCachedFolder(): TPromise<boolean> {
return pfs.exists(this.cachedFolderPath)
.then(null, () => false)
.then(exists => exists ? exists : pfs.mkdirp(this.cachedFolderPath).then(() => true, () => false));
}
}
export class FolderConfiguration extends Disposable implements IFolderConfiguration {
protected readonly _onDidChange: Emitter<void> = this._register(new Emitter<void>());
readonly onDidChange: Event<void> = this._onDidChange.event;
private folderConfiguration: IFolderConfiguration;
private cachedFolderConfiguration: CachedFolderConfiguration;
private _loaded: boolean = false;
constructor(
readonly workspaceFolder: IWorkspaceFolder,
private readonly configFolderRelativePath: string,
private readonly workbenchState: WorkbenchState,
private environmentService: IEnvironmentService,
fileService?: IFileService
) {
super();
this.cachedFolderConfiguration = new CachedFolderConfiguration(this.workspaceFolder.uri, this.configFolderRelativePath, this.environmentService);
this.folderConfiguration = this.cachedFolderConfiguration;
if (fileService) {
this.folderConfiguration = new FileServiceBasedFolderConfiguration(this.workspaceFolder.uri, this.configFolderRelativePath, this.workbenchState, fileService);
} else if (this.workspaceFolder.uri.scheme === Schemas.file) {
this.folderConfiguration = new NodeBasedFolderConfiguration(this.workspaceFolder.uri, this.configFolderRelativePath, this.workbenchState);
}
this._register(this.folderConfiguration.onDidChange(e => this.onDidFolderConfigurationChange()));
}
loadConfiguration(): TPromise<ConfigurationModel> {
return this.folderConfiguration.loadConfiguration()
.then(model => {
this._loaded = this.folderConfiguration.loaded;
return model;
});
}
reprocess(): ConfigurationModel {
return this.folderConfiguration.reprocess();
}
get loaded(): boolean {
return this._loaded;
}
adopt(fileService: IFileService): TPromise<boolean> {
if (fileService) {
if (this.folderConfiguration instanceof CachedFolderConfiguration) {
return this.adoptFromCachedConfiguration(fileService);
}
if (this.folderConfiguration instanceof NodeBasedFolderConfiguration) {
return this.adoptFromNodeBasedConfiguration(fileService);
}
}
return new ConfigurationModelParser(null);
return TPromise.as(false);
}
private isWorkspaceConfigurationFile(folderRelativePath: string): boolean {
return [FOLDER_SETTINGS_PATH, WORKSPACE_STANDALONE_CONFIGURATIONS[TASKS_CONFIGURATION_KEY], WORKSPACE_STANDALONE_CONFIGURATIONS[LAUNCH_CONFIGURATION_KEY]].some(p => p === folderRelativePath);
private adoptFromCachedConfiguration(fileService: IFileService): TPromise<boolean> {
const folderConfiguration = new FileServiceBasedFolderConfiguration(this.workspaceFolder.uri, this.configFolderRelativePath, this.workbenchState, fileService);
return folderConfiguration.loadConfiguration()
.then(() => {
this.folderConfiguration = folderConfiguration;
this._register(this.folderConfiguration.onDidChange(e => this.onDidFolderConfigurationChange()));
this.updateCache();
return true;
});
}
private toResource(folderRelativePath: string): URI {
if (typeof folderRelativePath === 'string') {
return URI.file(paths.join(this.folder.fsPath, folderRelativePath));
private adoptFromNodeBasedConfiguration(fileService: IFileService): TPromise<boolean> {
const oldFolderConfiguration = this.folderConfiguration;
this.folderConfiguration = new FileServiceBasedFolderConfiguration(this.workspaceFolder.uri, this.configFolderRelativePath, this.workbenchState, fileService, <AbstractFolderConfiguration>oldFolderConfiguration);
oldFolderConfiguration.dispose();
this._register(this.folderConfiguration.onDidChange(e => this.onDidFolderConfigurationChange()));
return TPromise.as(false);
}
private onDidFolderConfigurationChange(): void {
this.updateCache();
this._onDidChange.fire();
}
private updateCache(): TPromise<void> {
if (this.workspaceFolder.uri.scheme !== Schemas.file && this.folderConfiguration instanceof FileServiceBasedFolderConfiguration) {
return this.folderConfiguration.loadConfiguration()
.then(configurationModel => this.cachedFolderConfiguration.updateConfiguration(configurationModel));
}
return null;
}
private toFolderRelativePath(resource: URI, toOSPath?: boolean): string {
if (this.contains(resource)) {
return paths.normalize(relative(this.folder.fsPath, resource.fsPath), toOSPath);
}
return null;
}
private contains(resource: URI): boolean {
if (resource) {
return paths.isEqualOrParent(resource.fsPath, this.folder.fsPath, !isLinux /* ignorecase */);
}
return false;
return TPromise.as(null);
}
}

View File

@@ -30,7 +30,6 @@ import { OVERRIDE_PROPERTY_PATTERN, IConfigurationRegistry, Extensions as Config
import { ICommandService } from 'vs/platform/commands/common/commands';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import { ITextModel } from 'vs/editor/common/model';
import { IChoiceService } from 'vs/platform/dialogs/common/dialogs';
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
export enum ConfigurationEditingErrorCode {
@@ -40,6 +39,11 @@ export enum ConfigurationEditingErrorCode {
*/
ERROR_UNKNOWN_KEY,
/**
* Error when trying to write an application setting into workspace settings.
*/
ERROR_INVALID_WORKSPACE_CONFIGURATION_APPLICATION,
/**
* Error when trying to write an invalid folder configuration key to folder settings.
*/
@@ -127,7 +131,6 @@ export class ConfigurationEditingService {
@IFileService private fileService: IFileService,
@ITextModelService private textModelResolverService: ITextModelService,
@ITextFileService private textFileService: ITextFileService,
@IChoiceService private choiceService: IChoiceService,
@INotificationService private notificationService: INotificationService,
@ICommandService private commandService: ICommandService,
@IWorkbenchEditorService private editorService: IWorkbenchEditorService
@@ -139,12 +142,12 @@ export class ConfigurationEditingService {
const operation = this.getConfigurationEditOperation(target, value, options.scopes || {});
return this.queue.queue(() => this.doWriteConfiguration(operation, options) // queue up writes to prevent race conditions
.then(() => null,
error => {
if (!options.donotNotifyError) {
this.onError(error, operation, options.scopes);
}
return TPromise.wrapError(error);
}));
error => {
if (!options.donotNotifyError) {
this.onError(error, operation, options.scopes);
}
return TPromise.wrapError(error);
}));
}
private doWriteConfiguration(operation: IConfigurationEditOperation, options: ConfigurationEditingOptions): TPromise<void> {
@@ -194,19 +197,19 @@ export class ConfigurationEditingService {
: operation.workspaceStandAloneConfigurationKey === LAUNCH_CONFIGURATION_KEY ? nls.localize('openLaunchConfiguration', "Open Launch Configuration")
: null;
if (openStandAloneConfigurationActionLabel) {
this.choiceService.choose(Severity.Error, error.message, [openStandAloneConfigurationActionLabel])
.then(option => {
if (option === 0) {
this.openFile(operation.resource);
}
});
this.notificationService.prompt(Severity.Error, error.message,
[{
label: openStandAloneConfigurationActionLabel,
run: () => this.openFile(operation.resource)
}]
);
} else {
this.choiceService.choose(Severity.Error, error.message, [nls.localize('open', "Open Settings")])
.then(option => {
if (option === 0) {
this.openSettings(operation);
}
});
this.notificationService.prompt(Severity.Error, error.message,
[{
label: nls.localize('open', "Open Settings"),
run: () => this.openSettings(operation)
}]
);
}
}
@@ -215,30 +218,30 @@ export class ConfigurationEditingService {
: operation.workspaceStandAloneConfigurationKey === LAUNCH_CONFIGURATION_KEY ? nls.localize('openLaunchConfiguration', "Open Launch Configuration")
: null;
if (openStandAloneConfigurationActionLabel) {
this.choiceService.choose(Severity.Error, error.message, [nls.localize('saveAndRetry', "Save and Retry"), openStandAloneConfigurationActionLabel])
.then(option => {
switch (option) {
case 0 /* Save & Retry */:
const key = operation.key ? `${operation.workspaceStandAloneConfigurationKey}.${operation.key}` : operation.workspaceStandAloneConfigurationKey;
this.writeConfiguration(operation.target, { key, value: operation.value }, <ConfigurationEditingOptions>{ force: true, scopes });
break;
case 1 /* Open Config */:
this.openFile(operation.resource);
break;
this.notificationService.prompt(Severity.Error, error.message,
[{
label: nls.localize('saveAndRetry', "Save and Retry"),
run: () => {
const key = operation.key ? `${operation.workspaceStandAloneConfigurationKey}.${operation.key}` : operation.workspaceStandAloneConfigurationKey;
this.writeConfiguration(operation.target, { key, value: operation.value }, <ConfigurationEditingOptions>{ force: true, scopes });
}
});
},
{
label: openStandAloneConfigurationActionLabel,
run: () => this.openFile(operation.resource)
}]
);
} else {
this.choiceService.choose(Severity.Error, error.message, [nls.localize('saveAndRetry', "Save and Retry"), nls.localize('open', "Open Settings")])
.then(option => {
switch (option) {
case 0 /* Save and Retry */:
this.writeConfiguration(operation.target, { key: operation.key, value: operation.value }, <ConfigurationEditingOptions>{ force: true, scopes });
break;
case 1 /* Open Settings */:
this.openSettings(operation);
break;
}
});
this.notificationService.prompt(Severity.Error, error.message,
[{
label: nls.localize('saveAndRetry', "Save and Retry"),
run: () => this.writeConfiguration(operation.target, { key: operation.key, value: operation.value }, <ConfigurationEditingOptions>{ force: true, scopes })
},
{
label: nls.localize('open', "Open Settings"),
run: () => this.openSettings(operation)
}]
);
}
}
@@ -276,6 +279,7 @@ export class ConfigurationEditingService {
// API constraints
case ConfigurationEditingErrorCode.ERROR_UNKNOWN_KEY: return nls.localize('errorUnknownKey', "Unable to write to {0} because {1} is not a registered configuration.", this.stringifyTarget(target), operation.key);
case ConfigurationEditingErrorCode.ERROR_INVALID_WORKSPACE_CONFIGURATION_APPLICATION: return nls.localize('errorInvalidWorkspaceConfigurationApplication', "Unable to write {0} to Workspace Settings. This setting can be written only into User settings.", operation.key);
case ConfigurationEditingErrorCode.ERROR_INVALID_FOLDER_CONFIGURATION: return nls.localize('errorInvalidFolderConfiguration', "Unable to write to Folder Settings because {0} does not support the folder resource scope.", operation.key);
case ConfigurationEditingErrorCode.ERROR_INVALID_USER_TARGET: return nls.localize('errorInvalidUserTarget', "Unable to write to User Settings because {0} does not support for global scope.", operation.key);
case ConfigurationEditingErrorCode.ERROR_INVALID_WORKSPACE_TARGET: return nls.localize('errorInvalidWorkspaceTarget', "Unable to write to Workspace Settings because {0} does not support for workspace scope in a multi folder workspace.", operation.key);
@@ -398,6 +402,15 @@ export class ConfigurationEditingService {
return this.wrapError(ConfigurationEditingErrorCode.ERROR_NO_WORKSPACE_OPENED, target, operation);
}
if (target === ConfigurationTarget.WORKSPACE) {
if (!operation.workspaceStandAloneConfigurationKey) {
const configurationProperties = Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration).getConfigurationProperties();
if (configurationProperties[operation.key].scope === ConfigurationScope.APPLICATION) {
return this.wrapError(ConfigurationEditingErrorCode.ERROR_INVALID_WORKSPACE_CONFIGURATION_APPLICATION, target, operation);
}
}
}
if (target === ConfigurationTarget.WORKSPACE_FOLDER) {
if (!operation.resource) {
return this.wrapError(ConfigurationEditingErrorCode.ERROR_INVALID_FOLDER_TARGET, target, operation);

View File

@@ -8,15 +8,15 @@ import URI from 'vs/base/common/uri';
import { TPromise } from 'vs/base/common/winjs.base';
import { dirname, basename } from 'path';
import * as assert from 'vs/base/common/assert';
import Event, { Emitter } from 'vs/base/common/event';
import { StrictResourceMap } from 'vs/base/common/map';
import { equals } from 'vs/base/common/objects';
import { Event, Emitter } from 'vs/base/common/event';
import { ResourceMap } from 'vs/base/common/map';
import { equals, deepClone } from 'vs/base/common/objects';
import { Disposable } from 'vs/base/common/lifecycle';
import { Queue } from 'vs/base/common/async';
import { stat, writeFile } from 'vs/base/node/pfs';
import { IJSONContributionRegistry, Extensions as JSONExtensions } from 'vs/platform/jsonschemas/common/jsonContributionRegistry';
import { IWorkspaceContextService, Workspace, WorkbenchState, IWorkspaceFolder, toWorkspaceFolders, IWorkspaceFoldersChangeEvent, WorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { FileChangesEvent } from 'vs/platform/files/common/files';
import { IFileService } from 'vs/platform/files/common/files';
import { isLinux } from 'vs/base/common/platform';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { ConfigurationChangeEvent, ConfigurationModel, DefaultConfigurationModel } from 'vs/platform/configuration/common/configurationModels';
@@ -24,7 +24,7 @@ import { IConfigurationChangeEvent, ConfigurationTarget, IConfigurationOverrides
import { Configuration, WorkspaceConfigurationChangeEvent, AllKeysConfigurationChangeEvent } from 'vs/workbench/services/configuration/common/configurationModels';
import { IWorkspaceConfigurationService, FOLDER_CONFIG_FOLDER_NAME, defaultSettingsSchemaId, userSettingsSchemaId, workspaceSettingsSchemaId, folderSettingsSchemaId } from 'vs/workbench/services/configuration/common/configuration';
import { Registry } from 'vs/platform/registry/common/platform';
import { IConfigurationNode, IConfigurationRegistry, Extensions, settingsSchema, resourceSettingsSchema, IConfigurationPropertySchema } from 'vs/platform/configuration/common/configurationRegistry';
import { IConfigurationNode, IConfigurationRegistry, Extensions, IConfigurationPropertySchema, allSettings, windowSettings, resourceSettings, applicationSettings } from 'vs/platform/configuration/common/configurationRegistry';
import { createHash } from 'crypto';
import { getWorkspaceLabel, IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier, IStoredWorkspaceFolder, isStoredWorkspaceFolder, IWorkspaceFolderCreationData } from 'vs/platform/workspaces/common/workspaces';
import { IWindowConfiguration } from 'vs/platform/windows/common/windows';
@@ -37,9 +37,10 @@ import { WorkspaceConfiguration, FolderConfiguration } from 'vs/workbench/servic
import { JSONEditingService } from 'vs/workbench/services/configuration/node/jsonEditingService';
import { Schemas } from 'vs/base/common/network';
import { massageFolderPathForWorkspace } from 'vs/platform/workspaces/node/workspaces';
import { distinct } from 'vs/base/common/arrays';
import { UserConfiguration } from 'vs/platform/configuration/node/configuration';
import { getBaseLabel } from 'vs/base/common/labels';
import { IJSONSchema, IJSONSchemaMap } from 'vs/base/common/jsonSchema';
import { localize } from 'vs/nls';
export class WorkspaceService extends Disposable implements IWorkspaceConfigurationService, IWorkspaceContextService {
@@ -50,7 +51,7 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat
private defaultConfiguration: DefaultConfigurationModel;
private userConfiguration: UserConfiguration;
private workspaceConfiguration: WorkspaceConfiguration;
private cachedFolderConfigs: StrictResourceMap<FolderConfiguration>;
private cachedFolderConfigs: ResourceMap<FolderConfiguration>;
private workspaceEditingQueue: Queue<void>;
@@ -66,6 +67,7 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat
protected readonly _onDidChangeWorkbenchState: Emitter<WorkbenchState> = this._register(new Emitter<WorkbenchState>());
public readonly onDidChangeWorkbenchState: Event<WorkbenchState> = this._onDidChangeWorkbenchState.event;
private fileService: IFileService;
private configurationEditingService: ConfigurationEditingService;
private jsonEditingService: JSONEditingService;
@@ -236,7 +238,9 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat
// Workspace Configuration Service Impl
getConfigurationData(): IConfigurationData {
return this._configuration.toData();
const configurationData = this._configuration.toData();
configurationData.isComplete = this.cachedFolderConfigs.values().every(c => c.loaded);
return configurationData;
}
getValue<T>(): T;
@@ -291,32 +295,31 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat
return this._configuration.keys();
}
getUnsupportedWorkspaceKeys(): string[] {
const unsupportedWorkspaceKeys = [...this.workspaceConfiguration.getUnsupportedKeys()];
for (const folder of this.workspace.folders) {
unsupportedWorkspaceKeys.push(...this.cachedFolderConfigs.get(folder.uri).getUnsupportedKeys());
}
return distinct(unsupportedWorkspaceKeys);
}
initialize(arg: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IWindowConfiguration): TPromise<any> {
return this.createWorkspace(arg)
.then(workspace => this.updateWorkspaceAndInitializeConfiguration(workspace));
}
setInstantiationService(instantiationService: IInstantiationService): void {
this.configurationEditingService = instantiationService.createInstance(ConfigurationEditingService);
this.jsonEditingService = instantiationService.createInstance(JSONEditingService);
acquireFileService(fileService: IFileService): void {
this.fileService = fileService;
const changedWorkspaceFolders: IWorkspaceFolder[] = [];
TPromise.join(this.cachedFolderConfigs.values()
.map(folderConfiguration => folderConfiguration.adopt(fileService)
.then(result => {
if (result) {
changedWorkspaceFolders.push(folderConfiguration.workspaceFolder);
}
})))
.then(() => {
for (const workspaceFolder of changedWorkspaceFolders) {
this.onWorkspaceFolderConfigurationChanged(workspaceFolder);
}
});
}
handleWorkspaceFileEvents(event: FileChangesEvent): TPromise<void> {
switch (this.getWorkbenchState()) {
case WorkbenchState.FOLDER:
return this.onSingleFolderFileChanges(event);
case WorkbenchState.WORKSPACE:
return this.onWorkspaceFileChanges(event);
}
return TPromise.as(void 0);
acquireInstantiationService(instantiationService: IInstantiationService): void {
this.configurationEditingService = instantiationService.createInstance(ConfigurationEditingService);
this.jsonEditingService = instantiationService.createInstance(JSONEditingService);
}
private createWorkspace(arg: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IWindowConfiguration): TPromise<Workspace> {
@@ -438,18 +441,18 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat
private loadConfiguration(): TPromise<void> {
// reset caches
this.cachedFolderConfigs = new StrictResourceMap<FolderConfiguration>();
this.cachedFolderConfigs = new ResourceMap<FolderConfiguration>();
const folders = this.workspace.folders;
return this.loadFolderConfigurations(folders)
.then((folderConfigurations) => {
let workspaceConfiguration = this.getWorkspaceConfigurationModel(folderConfigurations);
const folderConfigurationModels = new StrictResourceMap<ConfigurationModel>();
const folderConfigurationModels = new ResourceMap<ConfigurationModel>();
folderConfigurations.forEach((folderConfiguration, index) => folderConfigurationModels.set(folders[index].uri, folderConfiguration));
const currentConfiguration = this._configuration;
this._configuration = new Configuration(this.defaultConfiguration, this.userConfiguration.configurationModel, workspaceConfiguration, folderConfigurationModels, new ConfigurationModel(), new StrictResourceMap<ConfigurationModel>(), this.getWorkbenchState() !== WorkbenchState.EMPTY ? this.workspace : null); //TODO: Sandy Avoid passing null
this._configuration = new Configuration(this.defaultConfiguration, this.userConfiguration.configurationModel, workspaceConfiguration, folderConfigurationModels, new ConfigurationModel(), new ResourceMap<ConfigurationModel>(), this.getWorkbenchState() !== WorkbenchState.EMPTY ? this.workspace : null); //TODO: Sandy Avoid passing null
if (currentConfiguration) {
const changedKeys = this._configuration.compare(currentConfiguration);
@@ -489,15 +492,29 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat
private registerConfigurationSchemas(): void {
if (this.workspace) {
const jsonRegistry = Registry.as<IJSONContributionRegistry>(JSONExtensions.JSONContribution);
jsonRegistry.registerSchema(defaultSettingsSchemaId, settingsSchema);
jsonRegistry.registerSchema(userSettingsSchemaId, settingsSchema);
const convertToNotSuggestedProperties = (properties: IJSONSchemaMap, errorMessage: string): IJSONSchemaMap => {
return Object.keys(properties).reduce((result: IJSONSchemaMap, property) => {
result[property] = deepClone(properties[property]);
result[property].deprecationMessage = errorMessage;
return result;
}, {});
};
const allSettingsSchema: IJSONSchema = { properties: allSettings.properties, patternProperties: allSettings.patternProperties, additionalProperties: false, errorMessage: 'Unknown configuration setting' };
const unsupportedApplicationSettings = convertToNotSuggestedProperties(applicationSettings.properties, localize('unsupportedApplicationSetting', "This setting can be applied only in User Settings"));
const workspaceSettingsSchema: IJSONSchema = { properties: { ...unsupportedApplicationSettings, ...windowSettings.properties, ...resourceSettings.properties }, patternProperties: allSettings.patternProperties, additionalProperties: false, errorMessage: 'Unknown configuration setting' };
jsonRegistry.registerSchema(defaultSettingsSchemaId, allSettingsSchema);
jsonRegistry.registerSchema(userSettingsSchemaId, allSettingsSchema);
if (WorkbenchState.WORKSPACE === this.getWorkbenchState()) {
jsonRegistry.registerSchema(workspaceSettingsSchemaId, settingsSchema);
jsonRegistry.registerSchema(folderSettingsSchemaId, resourceSettingsSchema);
const unsupportedWindowSettings = convertToNotSuggestedProperties(windowSettings.properties, localize('unsupportedWindowSetting', "This setting cannot be applied now. It will be applied when you open this folder directly."));
const folderSettingsSchema: IJSONSchema = { properties: { ...unsupportedApplicationSettings, ...unsupportedWindowSettings, ...resourceSettings.properties }, patternProperties: allSettings.patternProperties, additionalProperties: false, errorMessage: 'Unknown configuration setting' };
jsonRegistry.registerSchema(workspaceSettingsSchemaId, workspaceSettingsSchema);
jsonRegistry.registerSchema(folderSettingsSchemaId, folderSettingsSchema);
} else {
jsonRegistry.registerSchema(workspaceSettingsSchemaId, settingsSchema);
jsonRegistry.registerSchema(folderSettingsSchemaId, settingsSchema);
jsonRegistry.registerSchema(workspaceSettingsSchemaId, workspaceSettingsSchema);
jsonRegistry.registerSchema(folderSettingsSchemaId, workspaceSettingsSchema);
}
}
}
@@ -526,33 +543,7 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat
return TPromise.as(null);
}
private onWorkspaceFileChanges(event: FileChangesEvent): TPromise<void> {
return TPromise.join(this.workspace.folders.map(folder =>
// handle file event for each folder
this.cachedFolderConfigs.get(folder.uri).handleWorkspaceFileEvents(event)
// Update folder configuration if handled
.then(folderConfiguration => folderConfiguration ? this._configuration.compareAndUpdateFolderConfiguration(folder.uri, folderConfiguration) : new ConfigurationChangeEvent()))
).then(changeEvents => {
const consolidateChangeEvent = changeEvents.reduce((consolidated, e) => consolidated.change(e), new ConfigurationChangeEvent());
this.triggerConfigurationChange(consolidateChangeEvent, ConfigurationTarget.WORKSPACE_FOLDER);
});
}
private onSingleFolderFileChanges(event: FileChangesEvent): TPromise<void> {
const folder = this.workspace.folders[0];
return this.cachedFolderConfigs.get(folder.uri).handleWorkspaceFileEvents(event)
.then(folderConfiguration => {
if (folderConfiguration) {
// File change handled
this._configuration.compareAndUpdateFolderConfiguration(folder.uri, folderConfiguration);
const workspaceChangedKeys = this._configuration.compareAndUpdateWorkspaceConfiguration(folderConfiguration);
this.triggerConfigurationChange(workspaceChangedKeys, ConfigurationTarget.WORKSPACE);
}
});
}
private onWorkspaceFolderConfigurationChanged(folder: IWorkspaceFolder, key?: string): TPromise<void> {
this.disposeFolderConfiguration(folder);
return this.loadFolderConfigurations([folder])
.then(([folderConfiguration]) => {
const folderChangedKeys = this._configuration.compareAndUpdateFolderConfiguration(folder.uri, folderConfiguration);
@@ -571,6 +562,8 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat
// Remove the configurations of deleted folders
for (const key of this.cachedFolderConfigs.keys()) {
if (!this.workspace.folders.filter(folder => folder.uri.toString() === key.toString())[0]) {
const folderConfiguration = this.cachedFolderConfigs.get(key);
folderConfiguration.dispose();
this.cachedFolderConfigs.delete(key);
changeEvent = changeEvent.change(this._configuration.compareAndDeleteFolderConfiguration(key));
}
@@ -591,8 +584,12 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat
private loadFolderConfigurations(folders: IWorkspaceFolder[]): TPromise<ConfigurationModel[]> {
return TPromise.join([...folders.map(folder => {
const folderConfiguration = new FolderConfiguration(folder.uri, this.workspaceSettingsRootFolder, this.getWorkbenchState());
this.cachedFolderConfigs.set(folder.uri, this._register(folderConfiguration));
let folderConfiguration = this.cachedFolderConfigs.get(folder.uri);
if (!folderConfiguration) {
folderConfiguration = new FolderConfiguration(folder, this.workspaceSettingsRootFolder, this.getWorkbenchState(), this.environmentService, this.fileService);
this._register(folderConfiguration.onDidChange(() => this.onWorkspaceFolderConfigurationChanged(folder)));
this.cachedFolderConfigs.set(folder.uri, this._register(folderConfiguration));
}
return folderConfiguration.loadConfiguration();
})]);
}
@@ -679,13 +676,6 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat
return path1 === path2;
}
private disposeFolderConfiguration(folder: IWorkspaceFolder): void {
const folderConfiguration = this.cachedFolderConfigs.get(folder.uri);
if (folderConfiguration) {
folderConfiguration.dispose();
}
}
}
interface IExportedConfigurationNode {

View File

@@ -13,12 +13,12 @@ import URI from 'vs/base/common/uri';
import { ConfigurationChangeEvent, ConfigurationModel } from 'vs/platform/configuration/common/configurationModels';
import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry';
import { StrictResourceMap } from 'vs/base/common/map';
import { ResourceMap } from 'vs/base/common/map';
suite('FolderSettingsModelParser', () => {
suiteSetup(() => {
const configurationRegistry = <IConfigurationRegistry>Registry.as(ConfigurationExtensions.Configuration);
const configurationRegistry = Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration);
configurationRegistry.registerConfiguration({
'id': 'FolderSettingsModelParser_1',
'type': 'object',
@@ -32,47 +32,47 @@ suite('FolderSettingsModelParser', () => {
'default': 'isSet',
scope: ConfigurationScope.RESOURCE
},
'FolderSettingsModelParser.executable': {
'FolderSettingsModelParser.application': {
'type': 'string',
'default': 'isSet',
isExecutable: true
scope: ConfigurationScope.APPLICATION
}
}
});
});
test('parse all folder settings', () => {
const testObject = new FolderSettingsModelParser('settings');
const testObject = new FolderSettingsModelParser('settings', [ConfigurationScope.RESOURCE, ConfigurationScope.WINDOW]);
testObject.parse(JSON.stringify({ 'FolderSettingsModelParser.window': 'window', 'FolderSettingsModelParser.resource': 'resource', 'FolderSettingsModelParser.executable': 'executable' }));
testObject.parse(JSON.stringify({ 'FolderSettingsModelParser.window': 'window', 'FolderSettingsModelParser.resource': 'resource', 'FolderSettingsModelParser.application': 'executable' }));
assert.deepEqual(testObject.configurationModel.contents, { 'FolderSettingsModelParser': { 'window': 'window', 'resource': 'resource' } });
});
test('parse resource folder settings', () => {
const testObject = new FolderSettingsModelParser('settings', ConfigurationScope.RESOURCE);
const testObject = new FolderSettingsModelParser('settings', [ConfigurationScope.RESOURCE]);
testObject.parse(JSON.stringify({ 'FolderSettingsModelParser.window': 'window', 'FolderSettingsModelParser.resource': 'resource', 'FolderSettingsModelParser.executable': 'executable' }));
testObject.parse(JSON.stringify({ 'FolderSettingsModelParser.window': 'window', 'FolderSettingsModelParser.resource': 'resource', 'FolderSettingsModelParser.application': 'executable' }));
assert.deepEqual(testObject.configurationModel.contents, { 'FolderSettingsModelParser': { 'resource': 'resource' } });
});
test('reprocess folder settings excludes executable', () => {
const testObject = new FolderSettingsModelParser('settings');
test('reprocess folder settings excludes application setting', () => {
const testObject = new FolderSettingsModelParser('settings', [ConfigurationScope.RESOURCE, ConfigurationScope.WINDOW]);
testObject.parse(JSON.stringify({ 'FolderSettingsModelParser.resource': 'resource', 'FolderSettingsModelParser.anotherExecutable': 'executable' }));
testObject.parse(JSON.stringify({ 'FolderSettingsModelParser.resource': 'resource', 'FolderSettingsModelParser.anotherApplicationSetting': 'executable' }));
assert.deepEqual(testObject.configurationModel.contents, { 'FolderSettingsModelParser': { 'resource': 'resource', 'anotherExecutable': 'executable' } });
assert.deepEqual(testObject.configurationModel.contents, { 'FolderSettingsModelParser': { 'resource': 'resource', 'anotherApplicationSetting': 'executable' } });
const configurationRegistry = <IConfigurationRegistry>Registry.as(ConfigurationExtensions.Configuration);
const configurationRegistry = Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration);
configurationRegistry.registerConfiguration({
'id': 'FolderSettingsModelParser_2',
'type': 'object',
'properties': {
'FolderSettingsModelParser.anotherExecutable': {
'FolderSettingsModelParser.anotherApplicationSetting': {
'type': 'string',
'default': 'isSet',
isExecutable: true
scope: ConfigurationScope.APPLICATION
}
}
});
@@ -194,7 +194,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 StrictResourceMap(), new ConfigurationModel(), new StrictResourceMap(), 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']);
@@ -234,4 +234,4 @@ suite('AllKeysConfigurationChangeEvent', () => {
assert.ok(!testObject.affectsConfiguration('files'));
assert.ok(!testObject.affectsConfiguration('files', URI.file('file1')));
});
});
});

View File

@@ -6,10 +6,10 @@
'use strict';
import * as sinon from 'sinon';
import assert = require('assert');
import os = require('os');
import path = require('path');
import fs = require('fs');
import * as assert from 'assert';
import * as os from 'os';
import * as path from 'path';
import * as fs from 'fs';
import * as json from 'vs/base/common/json';
import { TPromise } from 'vs/base/common/winjs.base';
import { Registry } from 'vs/platform/registry/common/platform';
@@ -17,12 +17,13 @@ import { ParsedArgs, IEnvironmentService } from 'vs/platform/environment/common/
import { parseArgs } from 'vs/platform/environment/node/argv';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { EnvironmentService } from 'vs/platform/environment/node/environmentService';
import extfs = require('vs/base/node/extfs');
import { TestTextFileService, TestTextResourceConfigurationService, workbenchInstantiationService, TestLifecycleService, TestEnvironmentService } from 'vs/workbench/test/workbenchTestServices';
import uuid = require('vs/base/common/uuid');
import * as extfs from 'vs/base/node/extfs';
import { TestTextFileService, TestTextResourceConfigurationService, workbenchInstantiationService, TestLifecycleService, TestEnvironmentService, TestStorageService } from 'vs/workbench/test/workbenchTestServices';
import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService';
import * as uuid from 'vs/base/common/uuid';
import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry';
import { WorkspaceService } from 'vs/workbench/services/configuration/node/configurationService';
import { FileService } from 'vs/workbench/services/files/node/fileService';
import { FileService } from 'vs/workbench/services/files/electron-browser/fileService';
import { ConfigurationEditingService, ConfigurationEditingError, ConfigurationEditingErrorCode } from 'vs/workbench/services/configuration/node/configurationEditingService';
import { IFileService } from 'vs/platform/files/common/files';
import { WORKSPACE_STANDALONE_CONFIGURATIONS } from 'vs/workbench/services/configuration/common/configuration';
@@ -34,7 +35,9 @@ import { TextModelResolverService } from 'vs/workbench/services/textmodelResolve
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
import { IWindowConfiguration } from 'vs/platform/windows/common/windows';
import { mkdirp } from 'vs/base/node/pfs';
import { IChoiceService } from 'vs/platform/dialogs/common/dialogs';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { CommandService } from 'vs/workbench/services/commands/common/commandService';
class SettingsTestEnvironmentService extends EnvironmentService {
@@ -55,7 +58,7 @@ suite('ConfigurationEditingService', () => {
let workspaceSettingsDir;
suiteSetup(() => {
const configurationRegistry = <IConfigurationRegistry>Registry.as(ConfigurationExtensions.Configuration);
const configurationRegistry = Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration);
configurationRegistry.registerConfiguration({
'id': '_test',
'type': 'object',
@@ -103,9 +106,10 @@ suite('ConfigurationEditingService', () => {
instantiationService.stub(IWorkspaceContextService, workspaceService);
return workspaceService.initialize(noWorkspace ? {} as IWindowConfiguration : workspaceDir).then(() => {
instantiationService.stub(IConfigurationService, workspaceService);
instantiationService.stub(IFileService, new FileService(workspaceService, TestEnvironmentService, new TestTextResourceConfigurationService(), new TestConfigurationService(), new TestLifecycleService(), { disableWatcher: true }));
instantiationService.stub(IFileService, new FileService(workspaceService, TestEnvironmentService, new TestTextResourceConfigurationService(), new TestConfigurationService(), new TestLifecycleService(), new TestStorageService(), new TestNotificationService(), { disableWatcher: true }));
instantiationService.stub(ITextFileService, instantiationService.createInstance(TestTextFileService));
instantiationService.stub(ITextModelService, <ITextModelService>instantiationService.createInstance(TextModelResolverService));
instantiationService.stub(ICommandService, CommandService);
testObject = instantiationService.createInstance(ConfigurationEditingService);
});
}
@@ -138,34 +142,34 @@ suite('ConfigurationEditingService', () => {
test('errors cases - invalid key', () => {
return testObject.writeConfiguration(ConfigurationTarget.WORKSPACE, { key: 'unknown.key', value: 'value' })
.then(() => assert.fail('Should fail with ERROR_UNKNOWN_KEY'),
(error: ConfigurationEditingError) => assert.equal(error.code, ConfigurationEditingErrorCode.ERROR_UNKNOWN_KEY));
(error: ConfigurationEditingError) => assert.equal(error.code, ConfigurationEditingErrorCode.ERROR_UNKNOWN_KEY));
});
test('errors cases - invalid target', () => {
return testObject.writeConfiguration(ConfigurationTarget.USER, { key: 'tasks.something', value: 'value' })
.then(() => assert.fail('Should fail with ERROR_INVALID_TARGET'),
(error: ConfigurationEditingError) => assert.equal(error.code, ConfigurationEditingErrorCode.ERROR_INVALID_USER_TARGET));
(error: ConfigurationEditingError) => assert.equal(error.code, ConfigurationEditingErrorCode.ERROR_INVALID_USER_TARGET));
});
test('errors cases - no workspace', () => {
return setUpServices(true)
.then(() => testObject.writeConfiguration(ConfigurationTarget.WORKSPACE, { key: 'configurationEditing.service.testSetting', value: 'value' }))
.then(() => assert.fail('Should fail with ERROR_NO_WORKSPACE_OPENED'),
(error: ConfigurationEditingError) => assert.equal(error.code, ConfigurationEditingErrorCode.ERROR_NO_WORKSPACE_OPENED));
(error: ConfigurationEditingError) => assert.equal(error.code, ConfigurationEditingErrorCode.ERROR_NO_WORKSPACE_OPENED));
});
test('errors cases - invalid configuration', () => {
fs.writeFileSync(globalSettingsFile, ',,,,,,,,,,,,,,');
return testObject.writeConfiguration(ConfigurationTarget.USER, { key: 'configurationEditing.service.testSetting', value: 'value' })
.then(() => assert.fail('Should fail with ERROR_INVALID_CONFIGURATION'),
(error: ConfigurationEditingError) => assert.equal(error.code, ConfigurationEditingErrorCode.ERROR_INVALID_CONFIGURATION));
(error: ConfigurationEditingError) => assert.equal(error.code, ConfigurationEditingErrorCode.ERROR_INVALID_CONFIGURATION));
});
test('errors cases - dirty', () => {
instantiationService.stub(ITextFileService, 'isDirty', true);
return testObject.writeConfiguration(ConfigurationTarget.USER, { key: 'configurationEditing.service.testSetting', value: 'value' })
.then(() => assert.fail('Should fail with ERROR_CONFIGURATION_FILE_DIRTY error.'),
(error: ConfigurationEditingError) => assert.equal(error.code, ConfigurationEditingErrorCode.ERROR_CONFIGURATION_FILE_DIRTY));
(error: ConfigurationEditingError) => assert.equal(error.code, ConfigurationEditingErrorCode.ERROR_CONFIGURATION_FILE_DIRTY));
});
test('dirty error is not thrown if not asked to save', () => {
@@ -177,13 +181,13 @@ suite('ConfigurationEditingService', () => {
test('do not notify error', () => {
instantiationService.stub(ITextFileService, 'isDirty', true);
const target = sinon.stub();
instantiationService.stubPromise(IChoiceService, 'choose', target);
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) => {
assert.equal(false, target.calledOnce);
assert.equal(error.code, ConfigurationEditingErrorCode.ERROR_CONFIGURATION_FILE_DIRTY);
});
(error: ConfigurationEditingError) => {
assert.equal(false, target.calledOnce);
assert.equal(error.code, ConfigurationEditingErrorCode.ERROR_CONFIGURATION_FILE_DIRTY);
});
});
test('write one setting - empty file', () => {

View File

@@ -16,16 +16,17 @@ import { Registry } from 'vs/platform/registry/common/platform';
import { ParsedArgs, IEnvironmentService } from 'vs/platform/environment/common/environment';
import { EnvironmentService } from 'vs/platform/environment/node/environmentService';
import { parseArgs } from 'vs/platform/environment/node/argv';
import extfs = require('vs/base/node/extfs');
import uuid = require('vs/base/common/uuid');
import * as pfs from 'vs/base/node/pfs';
import * as uuid from 'vs/base/common/uuid';
import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry';
import { WorkspaceService } from 'vs/workbench/services/configuration/node/configurationService';
import { ConfigurationEditingErrorCode } from 'vs/workbench/services/configuration/node/configurationEditingService';
import { FileChangeType, FileChangesEvent, IFileService } from 'vs/platform/files/common/files';
import { IFileService } 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 } from 'vs/workbench/test/workbenchTestServices';
import { FileService } from 'vs/workbench/services/files/node/fileService';
import { workbenchInstantiationService, TestTextResourceConfigurationService, TestTextFileService, TestLifecycleService, TestEnvironmentService, TestStorageService } from 'vs/workbench/test/workbenchTestServices';
import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService';
import { FileService } from 'vs/workbench/services/files/electron-browser/fileService';
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { ITextModelService } from 'vs/editor/common/services/resolverService';
@@ -34,7 +35,6 @@ import { IJSONEditingService } from 'vs/workbench/services/configuration/common/
import { JSONEditingService } from 'vs/workbench/services/configuration/node/jsonEditingService';
import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration';
import { IWindowConfiguration } from 'vs/platform/windows/common/windows';
import { mkdirp } from 'vs/base/node/pfs';
class SettingsTestEnvironmentService extends EnvironmentService {
@@ -55,7 +55,7 @@ function setUpFolder(folderName: string, parentDir: string): TPromise<string> {
const folderDir = path.join(parentDir, folderName);
// {{SQL CARBON EDIT}}
const workspaceSettingsDir = path.join(folderDir, '.sqlops');
return mkdirp(workspaceSettingsDir, 493).then(() => folderDir);
return pfs.mkdirp(workspaceSettingsDir, 493).then(() => folderDir);
}
function setUpWorkspace(folders: string[]): TPromise<{ parentDir: string, configPath: string }> {
@@ -63,7 +63,7 @@ function setUpWorkspace(folders: string[]): TPromise<{ parentDir: string, config
const id = uuid.generateUuid();
const parentDir = path.join(os.tmpdir(), 'vsctests', id);
return mkdirp(parentDir, 493)
return pfs.mkdirp(parentDir, 493)
.then(() => {
const configPath = path.join(parentDir, 'vsctests.code-workspace');
const workspace = { folders: folders.map(path => ({ path })) };