mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-02 09:35:40 -05:00
Merge VS Code 1.23.1 (#1520)
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user