mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 10:58:30 -05:00
Merge from vscode 2b0b9136329c181a9e381463a1f7dc3a2d105a34 (#4880)
This commit is contained in:
@@ -4,43 +4,41 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { createHash } from 'crypto';
|
||||
import * as resources from 'vs/base/common/resources';
|
||||
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, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { RunOnceScheduler, Delayer } from 'vs/base/common/async';
|
||||
import { FileChangeType, FileChangesEvent, IContent, IFileService } from 'vs/platform/files/common/files';
|
||||
import { ConfigurationModel } from 'vs/platform/configuration/common/configurationModels';
|
||||
import { RunOnceScheduler } from 'vs/base/common/async';
|
||||
import { FileChangeType, FileChangesEvent, IFileService } from 'vs/platform/files/common/files';
|
||||
import { ConfigurationModel, ConfigurationModelParser } from 'vs/platform/configuration/common/configurationModels';
|
||||
import { WorkspaceConfigurationModelParser, FolderSettingsModelParser, StandaloneConfigurationModelParser } from 'vs/workbench/services/configuration/common/configurationModels';
|
||||
import { FOLDER_SETTINGS_PATH, TASKS_CONFIGURATION_KEY, FOLDER_SETTINGS_NAME, LAUNCH_CONFIGURATION_KEY } from 'vs/workbench/services/configuration/common/configuration';
|
||||
import { IStoredWorkspaceFolder } from 'vs/platform/workspaces/common/workspaces';
|
||||
import * as extfs from 'vs/base/node/extfs';
|
||||
import { FOLDER_SETTINGS_PATH, TASKS_CONFIGURATION_KEY, FOLDER_SETTINGS_NAME, LAUNCH_CONFIGURATION_KEY, IConfigurationCache, ConfigurationKey, IConfigurationFileService } from 'vs/workbench/services/configuration/common/configuration';
|
||||
import { IStoredWorkspaceFolder, IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
|
||||
import { JSONEditingService } from 'vs/workbench/services/configuration/common/jsonEditingService';
|
||||
import { WorkbenchState, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
|
||||
import { ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
import { extname, join } from 'vs/base/common/path';
|
||||
import { equals } from 'vs/base/common/objects';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { IConfigurationModel, compare } from 'vs/platform/configuration/common/configuration';
|
||||
import { FileServiceBasedUserConfiguration, NodeBasedUserConfiguration } from 'vs/platform/configuration/node/configuration';
|
||||
import { createSHA1 } from 'vs/base/browser/hash';
|
||||
|
||||
export class LocalUserConfiguration extends Disposable {
|
||||
|
||||
private readonly userConfigurationResource: URI;
|
||||
private userConfiguration: NodeBasedUserConfiguration | FileServiceBasedUserConfiguration;
|
||||
private changeDisposable: IDisposable = Disposable.None;
|
||||
|
||||
private readonly _onDidChangeConfiguration: Emitter<ConfigurationModel> = this._register(new Emitter<ConfigurationModel>());
|
||||
public readonly onDidChangeConfiguration: Event<ConfigurationModel> = this._onDidChangeConfiguration.event;
|
||||
|
||||
constructor(
|
||||
environmentService: IEnvironmentService
|
||||
userConfigurationResource: URI,
|
||||
configurationFileService: IConfigurationFileService
|
||||
) {
|
||||
super();
|
||||
this.userConfiguration = this._register(new NodeBasedUserConfiguration(environmentService.appSettingsPath));
|
||||
this._register(this.userConfiguration.onDidChangeConfiguration(configurationModel => this._onDidChangeConfiguration.fire(configurationModel)));
|
||||
this.userConfigurationResource = userConfigurationResource;
|
||||
this.userConfiguration = this._register(new NodeBasedUserConfiguration(this.userConfigurationResource, configurationFileService));
|
||||
}
|
||||
|
||||
initialize(): Promise<ConfigurationModel> {
|
||||
@@ -52,6 +50,21 @@ export class LocalUserConfiguration extends Disposable {
|
||||
}
|
||||
|
||||
async adopt(fileService: IFileService): Promise<ConfigurationModel | null> {
|
||||
if (this.userConfiguration instanceof NodeBasedUserConfiguration) {
|
||||
const oldConfigurationModel = this.userConfiguration.getConfigurationModel();
|
||||
this.userConfiguration.dispose();
|
||||
dispose(this.changeDisposable);
|
||||
|
||||
let newConfigurationModel = new ConfigurationModel();
|
||||
this.userConfiguration = this._register(new FileServiceBasedUserConfiguration(this.userConfigurationResource, fileService));
|
||||
this.changeDisposable = this._register(this.userConfiguration.onDidChangeConfiguration(configurationModel => this._onDidChangeConfiguration.fire(configurationModel)));
|
||||
newConfigurationModel = await this.userConfiguration.initialize();
|
||||
|
||||
const { added, updated, removed } = compare(oldConfigurationModel, newConfigurationModel);
|
||||
if (added.length > 0 || updated.length > 0 || removed.length > 0) {
|
||||
return newConfigurationModel;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -66,10 +79,10 @@ export class RemoteUserConfiguration extends Disposable {
|
||||
|
||||
constructor(
|
||||
remoteAuthority: string,
|
||||
environmentService: IEnvironmentService
|
||||
configurationCache: IConfigurationCache
|
||||
) {
|
||||
super();
|
||||
this._userConfiguration = this._cachedConfiguration = new CachedUserConfiguration(remoteAuthority, environmentService);
|
||||
this._userConfiguration = this._cachedConfiguration = new CachedUserConfiguration(remoteAuthority, configurationCache);
|
||||
}
|
||||
|
||||
initialize(): Promise<ConfigurationModel> {
|
||||
@@ -108,22 +121,157 @@ export class RemoteUserConfiguration extends Disposable {
|
||||
}
|
||||
}
|
||||
|
||||
class NodeBasedUserConfiguration extends Disposable {
|
||||
|
||||
private configuraitonModel: ConfigurationModel = new ConfigurationModel();
|
||||
|
||||
constructor(
|
||||
private readonly userConfigurationResource: URI,
|
||||
private readonly configurationFileService: IConfigurationFileService
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
initialize(): Promise<ConfigurationModel> {
|
||||
return this._load();
|
||||
}
|
||||
|
||||
reload(): Promise<ConfigurationModel> {
|
||||
return this._load();
|
||||
}
|
||||
|
||||
getConfigurationModel(): ConfigurationModel {
|
||||
return this.configuraitonModel;
|
||||
}
|
||||
|
||||
async _load(): Promise<ConfigurationModel> {
|
||||
const exists = await this.configurationFileService.exists(this.userConfigurationResource);
|
||||
if (exists) {
|
||||
try {
|
||||
const content = await this.configurationFileService.resolveContent(this.userConfigurationResource);
|
||||
const parser = new ConfigurationModelParser(this.userConfigurationResource.toString());
|
||||
parser.parse(content);
|
||||
this.configuraitonModel = parser.configurationModel;
|
||||
} catch (e) {
|
||||
// ignore error
|
||||
errors.onUnexpectedError(e);
|
||||
this.configuraitonModel = new ConfigurationModel();
|
||||
}
|
||||
} else {
|
||||
this.configuraitonModel = new ConfigurationModel();
|
||||
}
|
||||
return this.configuraitonModel;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export class FileServiceBasedUserConfiguration extends Disposable {
|
||||
|
||||
private readonly reloadConfigurationScheduler: RunOnceScheduler;
|
||||
protected readonly _onDidChangeConfiguration: Emitter<ConfigurationModel> = this._register(new Emitter<ConfigurationModel>());
|
||||
readonly onDidChangeConfiguration: Event<ConfigurationModel> = this._onDidChangeConfiguration.event;
|
||||
|
||||
private fileWatcherDisposable: IDisposable = Disposable.None;
|
||||
private directoryWatcherDisposable: IDisposable = Disposable.None;
|
||||
|
||||
constructor(
|
||||
private readonly configurationResource: URI,
|
||||
private readonly fileService: IFileService
|
||||
) {
|
||||
super();
|
||||
|
||||
this._register(fileService.onFileChanges(e => this.handleFileEvents(e)));
|
||||
this.reloadConfigurationScheduler = this._register(new RunOnceScheduler(() => this.reload().then(configurationModel => this._onDidChangeConfiguration.fire(configurationModel)), 50));
|
||||
this._register(toDisposable(() => {
|
||||
this.stopWatchingResource();
|
||||
this.stopWatchingDirectory();
|
||||
}));
|
||||
}
|
||||
|
||||
private watchResource(): void {
|
||||
this.fileWatcherDisposable = this.fileService.watch(this.configurationResource);
|
||||
}
|
||||
|
||||
private stopWatchingResource(): void {
|
||||
this.fileWatcherDisposable.dispose();
|
||||
this.fileWatcherDisposable = Disposable.None;
|
||||
}
|
||||
|
||||
private watchDirectory(): void {
|
||||
const directory = resources.dirname(this.configurationResource);
|
||||
this.directoryWatcherDisposable = this.fileService.watch(directory);
|
||||
}
|
||||
|
||||
private stopWatchingDirectory(): void {
|
||||
this.directoryWatcherDisposable.dispose();
|
||||
this.directoryWatcherDisposable = Disposable.None;
|
||||
}
|
||||
|
||||
async initialize(): Promise<ConfigurationModel> {
|
||||
const exists = await this.fileService.exists(this.configurationResource);
|
||||
this.onResourceExists(exists);
|
||||
return this.reload();
|
||||
}
|
||||
|
||||
async reload(): Promise<ConfigurationModel> {
|
||||
try {
|
||||
const content = await this.fileService.resolveContent(this.configurationResource);
|
||||
const parser = new ConfigurationModelParser(this.configurationResource.toString());
|
||||
parser.parse(content.value);
|
||||
return parser.configurationModel;
|
||||
} catch (e) {
|
||||
return new ConfigurationModel();
|
||||
}
|
||||
}
|
||||
|
||||
private async handleFileEvents(event: FileChangesEvent): Promise<void> {
|
||||
const events = event.changes;
|
||||
|
||||
let affectedByChanges = false;
|
||||
|
||||
// Find changes that affect the resource
|
||||
for (const event of events) {
|
||||
affectedByChanges = resources.isEqual(this.configurationResource, event.resource);
|
||||
if (affectedByChanges) {
|
||||
if (event.type === FileChangeType.ADDED) {
|
||||
this.onResourceExists(true);
|
||||
} else if (event.type === FileChangeType.DELETED) {
|
||||
this.onResourceExists(false);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (affectedByChanges) {
|
||||
this.reloadConfigurationScheduler.schedule();
|
||||
}
|
||||
}
|
||||
|
||||
private onResourceExists(exists: boolean): void {
|
||||
if (exists) {
|
||||
this.stopWatchingDirectory();
|
||||
this.watchResource();
|
||||
} else {
|
||||
this.stopWatchingResource();
|
||||
this.watchDirectory();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class CachedUserConfiguration extends Disposable {
|
||||
|
||||
private readonly _onDidChange: Emitter<ConfigurationModel> = this._register(new Emitter<ConfigurationModel>());
|
||||
readonly onDidChange: Event<ConfigurationModel> = this._onDidChange.event;
|
||||
|
||||
private readonly cachedFolderPath: string;
|
||||
private readonly cachedConfigurationPath: string;
|
||||
private readonly key: ConfigurationKey;
|
||||
private configurationModel: ConfigurationModel;
|
||||
|
||||
constructor(
|
||||
remoteAuthority: string,
|
||||
private environmentService: IEnvironmentService
|
||||
private readonly configurationCache: IConfigurationCache
|
||||
) {
|
||||
super();
|
||||
this.cachedFolderPath = join(this.environmentService.userDataPath, 'CachedConfigurations', 'user', remoteAuthority);
|
||||
this.cachedConfigurationPath = join(this.cachedFolderPath, 'configuration.json');
|
||||
this.key = { type: 'user', key: remoteAuthority };
|
||||
this.configurationModel = new ConfigurationModel();
|
||||
}
|
||||
|
||||
@@ -135,44 +283,29 @@ class CachedUserConfiguration extends Disposable {
|
||||
return this.reload();
|
||||
}
|
||||
|
||||
reload(): Promise<ConfigurationModel> {
|
||||
return pfs.readFile(this.cachedConfigurationPath)
|
||||
.then(content => content.toString(), () => '')
|
||||
.then(content => {
|
||||
try {
|
||||
const parsed: IConfigurationModel = JSON.parse(content);
|
||||
this.configurationModel = new ConfigurationModel(parsed.contents, parsed.keys, parsed.overrides);
|
||||
} catch (e) {
|
||||
}
|
||||
return this.configurationModel;
|
||||
});
|
||||
async reload(): Promise<ConfigurationModel> {
|
||||
const content = await this.configurationCache.read(this.key);
|
||||
try {
|
||||
const parsed: IConfigurationModel = JSON.parse(content);
|
||||
this.configurationModel = new ConfigurationModel(parsed.contents, parsed.keys, parsed.overrides);
|
||||
} catch (e) {
|
||||
}
|
||||
return this.configurationModel;
|
||||
}
|
||||
|
||||
updateConfiguration(configurationModel: ConfigurationModel): Promise<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 undefined;
|
||||
});
|
||||
if (configurationModel.keys.length) {
|
||||
return this.configurationCache.write(this.key, JSON.stringify(configurationModel.toJSON()));
|
||||
} else {
|
||||
return this.configurationCache.remove(this.key);
|
||||
}
|
||||
}
|
||||
|
||||
private createCachedFolder(): Promise<boolean> {
|
||||
return Promise.resolve(pfs.exists(this.cachedFolderPath))
|
||||
.then(undefined, () => false)
|
||||
.then(exists => exists ? exists : pfs.mkdirp(this.cachedFolderPath).then(() => true, () => false));
|
||||
}
|
||||
}
|
||||
|
||||
export interface IWorkspaceIdentifier {
|
||||
id: string;
|
||||
configPath: URI;
|
||||
}
|
||||
|
||||
export class WorkspaceConfiguration extends Disposable {
|
||||
|
||||
private readonly _cachedConfiguration: CachedWorkspaceConfiguration;
|
||||
private readonly _configurationFileService: IConfigurationFileService;
|
||||
private _workspaceConfiguration: IWorkspaceConfiguration;
|
||||
private _workspaceIdentifier: IWorkspaceIdentifier | null = null;
|
||||
private _fileService: IFileService | null = null;
|
||||
@@ -181,10 +314,12 @@ export class WorkspaceConfiguration extends Disposable {
|
||||
public readonly onDidUpdateConfiguration: Event<void> = this._onDidUpdateConfiguration.event;
|
||||
|
||||
constructor(
|
||||
environmentService: IEnvironmentService
|
||||
configurationCache: IConfigurationCache,
|
||||
configurationFileService: IConfigurationFileService
|
||||
) {
|
||||
super();
|
||||
this._cachedConfiguration = new CachedWorkspaceConfiguration(environmentService);
|
||||
this._cachedConfiguration = new CachedWorkspaceConfiguration(configurationCache);
|
||||
this._configurationFileService = configurationFileService;
|
||||
this._workspaceConfiguration = this._cachedConfiguration;
|
||||
}
|
||||
|
||||
@@ -251,7 +386,7 @@ export class WorkspaceConfiguration extends Disposable {
|
||||
if (this._workspaceIdentifier.configPath.scheme === Schemas.file) {
|
||||
if (!(this._workspaceConfiguration instanceof NodeBasedWorkspaceConfiguration)) {
|
||||
dispose(this._workspaceConfiguration);
|
||||
this._workspaceConfiguration = new NodeBasedWorkspaceConfiguration();
|
||||
this._workspaceConfiguration = new NodeBasedWorkspaceConfiguration(this._configurationFileService);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -306,14 +441,17 @@ abstract class AbstractWorkspaceConfiguration extends Disposable implements IWor
|
||||
return this._workspaceIdentifier;
|
||||
}
|
||||
|
||||
load(workspaceIdentifier: IWorkspaceIdentifier): Promise<void> {
|
||||
async load(workspaceIdentifier: IWorkspaceIdentifier): Promise<void> {
|
||||
this._workspaceIdentifier = workspaceIdentifier;
|
||||
return this.loadWorkspaceConfigurationContents(workspaceIdentifier)
|
||||
.then(contents => {
|
||||
this.workspaceConfigurationModelParser = new WorkspaceConfigurationModelParser(workspaceIdentifier.id);
|
||||
this.workspaceConfigurationModelParser.parse(contents);
|
||||
this.consolidate();
|
||||
});
|
||||
this.workspaceConfigurationModelParser = new WorkspaceConfigurationModelParser(workspaceIdentifier.id);
|
||||
let contents = '';
|
||||
try {
|
||||
contents = (await this.loadWorkspaceConfigurationContents(workspaceIdentifier.configPath)) || '';
|
||||
} catch (e) {
|
||||
errors.onUnexpectedError(e);
|
||||
}
|
||||
this.workspaceConfigurationModelParser.parse(contents);
|
||||
this.consolidate();
|
||||
}
|
||||
|
||||
getConfigurationModel(): ConfigurationModel {
|
||||
@@ -338,17 +476,21 @@ abstract class AbstractWorkspaceConfiguration extends Disposable implements IWor
|
||||
this.workspaceSettings = this.workspaceConfigurationModelParser.settingsModel.merge(this.workspaceConfigurationModelParser.launchModel);
|
||||
}
|
||||
|
||||
protected abstract loadWorkspaceConfigurationContents(workspaceIdentifier: IWorkspaceIdentifier): Promise<string>;
|
||||
protected abstract loadWorkspaceConfigurationContents(workspaceConfigurationResource: URI): Promise<string | undefined>;
|
||||
}
|
||||
|
||||
class NodeBasedWorkspaceConfiguration extends AbstractWorkspaceConfiguration {
|
||||
|
||||
protected loadWorkspaceConfigurationContents(workspaceIdentifier: IWorkspaceIdentifier): Promise<string> {
|
||||
return pfs.readFile(workspaceIdentifier.configPath.fsPath)
|
||||
.then(contents => contents.toString(), e => {
|
||||
errors.onUnexpectedError(e);
|
||||
return '';
|
||||
});
|
||||
constructor(private readonly configurationFileService: IConfigurationFileService) {
|
||||
super();
|
||||
}
|
||||
|
||||
protected async loadWorkspaceConfigurationContents(workspaceConfigurationResource: URI): Promise<string | undefined> {
|
||||
const exists = await this.configurationFileService.exists(workspaceConfigurationResource);
|
||||
if (exists) {
|
||||
return this.configurationFileService.resolveContent(workspaceConfigurationResource);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -356,6 +498,8 @@ class NodeBasedWorkspaceConfiguration extends AbstractWorkspaceConfiguration {
|
||||
class FileServiceBasedWorkspaceConfiguration extends AbstractWorkspaceConfiguration {
|
||||
|
||||
private workspaceConfig: URI | null = null;
|
||||
private workspaceConfigWatcher: IDisposable;
|
||||
|
||||
private readonly reloadConfigurationScheduler: RunOnceScheduler;
|
||||
|
||||
constructor(private fileService: IFileService, from?: IWorkspaceConfiguration) {
|
||||
@@ -363,33 +507,24 @@ class FileServiceBasedWorkspaceConfiguration extends AbstractWorkspaceConfigurat
|
||||
this.workspaceConfig = from && from.workspaceIdentifier ? from.workspaceIdentifier.configPath : null;
|
||||
this._register(fileService.onFileChanges(e => this.handleWorkspaceFileEvents(e)));
|
||||
this.reloadConfigurationScheduler = this._register(new RunOnceScheduler(() => this._onDidChange.fire(), 50));
|
||||
this.watchWorkspaceConfigurationFile();
|
||||
this._register(toDisposable(() => this.unWatchWorkspaceConfigurtionFile()));
|
||||
this.workspaceConfigWatcher = this.watchWorkspaceConfigurationFile();
|
||||
}
|
||||
|
||||
private watchWorkspaceConfigurationFile(): void {
|
||||
private watchWorkspaceConfigurationFile(): IDisposable {
|
||||
if (this.workspaceConfig) {
|
||||
this.fileService.watch(this.workspaceConfig);
|
||||
return this.fileService.watch(this.workspaceConfig);
|
||||
}
|
||||
|
||||
return Disposable.None;
|
||||
}
|
||||
|
||||
private unWatchWorkspaceConfigurtionFile(): void {
|
||||
if (this.workspaceConfig) {
|
||||
this.fileService.unwatch(this.workspaceConfig);
|
||||
protected loadWorkspaceConfigurationContents(workspaceConfigurationResource: URI): Promise<string> {
|
||||
if (!(this.workspaceConfig && resources.isEqual(this.workspaceConfig, workspaceConfigurationResource))) {
|
||||
dispose(this.workspaceConfigWatcher);
|
||||
this.workspaceConfig = workspaceConfigurationResource;
|
||||
this.workspaceConfigWatcher = this.watchWorkspaceConfigurationFile();
|
||||
}
|
||||
}
|
||||
|
||||
protected loadWorkspaceConfigurationContents(workspaceIdentifier: IWorkspaceIdentifier): Promise<string> {
|
||||
if (!(this.workspaceConfig && resources.isEqual(this.workspaceConfig, workspaceIdentifier.configPath))) {
|
||||
this.unWatchWorkspaceConfigurtionFile();
|
||||
this.workspaceConfig = workspaceIdentifier.configPath;
|
||||
this.watchWorkspaceConfigurationFile();
|
||||
}
|
||||
return this.fileService.resolveContent(this.workspaceConfig)
|
||||
.then(content => content.value, e => {
|
||||
errors.onUnexpectedError(e);
|
||||
return '';
|
||||
});
|
||||
return this.fileService.resolveContent(this.workspaceConfig).then(content => content.value);
|
||||
}
|
||||
|
||||
private handleWorkspaceFileEvents(event: FileChangesEvent): void {
|
||||
@@ -407,6 +542,12 @@ class FileServiceBasedWorkspaceConfiguration extends AbstractWorkspaceConfigurat
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
super.dispose();
|
||||
|
||||
dispose(this.workspaceConfigWatcher);
|
||||
}
|
||||
}
|
||||
|
||||
class CachedWorkspaceConfiguration extends Disposable implements IWorkspaceConfiguration {
|
||||
@@ -414,25 +555,24 @@ class CachedWorkspaceConfiguration extends Disposable implements IWorkspaceConfi
|
||||
private readonly _onDidChange: Emitter<void> = this._register(new Emitter<void>());
|
||||
readonly onDidChange: Event<void> = this._onDidChange.event;
|
||||
|
||||
private cachedWorkspacePath: string;
|
||||
private cachedConfigurationPath: string;
|
||||
workspaceConfigurationModelParser: WorkspaceConfigurationModelParser;
|
||||
workspaceSettings: ConfigurationModel;
|
||||
|
||||
constructor(private environmentService: IEnvironmentService) {
|
||||
constructor(private readonly configurationCache: IConfigurationCache) {
|
||||
super();
|
||||
this.workspaceConfigurationModelParser = new WorkspaceConfigurationModelParser('');
|
||||
this.workspaceSettings = new ConfigurationModel();
|
||||
}
|
||||
|
||||
load(workspaceIdentifier: IWorkspaceIdentifier): Promise<void> {
|
||||
this.createPaths(workspaceIdentifier);
|
||||
return pfs.readFile(this.cachedConfigurationPath)
|
||||
.then(contents => {
|
||||
this.workspaceConfigurationModelParser = new WorkspaceConfigurationModelParser(this.cachedConfigurationPath);
|
||||
this.workspaceConfigurationModelParser.parse(contents.toString());
|
||||
this.workspaceSettings = this.workspaceConfigurationModelParser.settingsModel.merge(this.workspaceConfigurationModelParser.launchModel);
|
||||
}, () => { });
|
||||
async load(workspaceIdentifier: IWorkspaceIdentifier): Promise<void> {
|
||||
try {
|
||||
const key = this.getKey(workspaceIdentifier);
|
||||
const contents = await this.configurationCache.read(key);
|
||||
this.workspaceConfigurationModelParser = new WorkspaceConfigurationModelParser(key.key);
|
||||
this.workspaceConfigurationModelParser.parse(contents);
|
||||
this.workspaceSettings = this.workspaceConfigurationModelParser.settingsModel.merge(this.workspaceConfigurationModelParser.launchModel);
|
||||
} catch (e) {
|
||||
}
|
||||
}
|
||||
|
||||
get workspaceIdentifier(): IWorkspaceIdentifier | null {
|
||||
@@ -457,38 +597,24 @@ class CachedWorkspaceConfiguration extends Disposable implements IWorkspaceConfi
|
||||
|
||||
async updateWorkspace(workspaceIdentifier: IWorkspaceIdentifier, configurationModel: ConfigurationModel): Promise<void> {
|
||||
try {
|
||||
this.createPaths(workspaceIdentifier);
|
||||
const key = this.getKey(workspaceIdentifier);
|
||||
if (configurationModel.keys.length) {
|
||||
const exists = await pfs.exists(this.cachedWorkspacePath);
|
||||
if (!exists) {
|
||||
await pfs.mkdirp(this.cachedWorkspacePath);
|
||||
}
|
||||
const raw = JSON.stringify(configurationModel.toJSON().contents);
|
||||
await pfs.writeFile(this.cachedConfigurationPath, raw);
|
||||
await this.configurationCache.write(key, JSON.stringify(configurationModel.toJSON().contents));
|
||||
} else {
|
||||
pfs.rimraf(this.cachedWorkspacePath);
|
||||
await this.configurationCache.remove(key);
|
||||
}
|
||||
} catch (error) {
|
||||
errors.onUnexpectedError(error);
|
||||
}
|
||||
}
|
||||
|
||||
private createPaths(workspaceIdentifier: IWorkspaceIdentifier) {
|
||||
this.cachedWorkspacePath = join(this.environmentService.userDataPath, 'CachedConfigurations', 'workspaces', workspaceIdentifier.id);
|
||||
this.cachedConfigurationPath = join(this.cachedWorkspacePath, 'workspace.json');
|
||||
private getKey(workspaceIdentifier: IWorkspaceIdentifier): ConfigurationKey {
|
||||
return {
|
||||
type: 'workspaces',
|
||||
key: workspaceIdentifier.id
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function isFolderConfigurationFile(resource: URI): boolean {
|
||||
const configurationNameResource = URI.from({ scheme: resource.scheme, path: resources.basename(resource) });
|
||||
return [`${FOLDER_SETTINGS_NAME}.json`, `${TASKS_CONFIGURATION_KEY}.json`, `${LAUNCH_CONFIGURATION_KEY}.json`].some(configurationFileName =>
|
||||
resources.isEqual(configurationNameResource, URI.from({ scheme: resource.scheme, path: configurationFileName }))); // only workspace config files
|
||||
}
|
||||
|
||||
function isFolderSettingsConfigurationFile(resource: URI): boolean {
|
||||
return resources.isEqual(URI.from({ scheme: resource.scheme, path: resources.basename(resource) }), URI.from({ scheme: resource.scheme, path: `${FOLDER_SETTINGS_NAME}.json` }));
|
||||
}
|
||||
|
||||
export interface IFolderConfiguration extends IDisposable {
|
||||
readonly onDidChange: Event<void>;
|
||||
readonly loaded: boolean;
|
||||
@@ -503,12 +629,16 @@ export abstract class AbstractFolderConfiguration extends Disposable implements
|
||||
private _cache: ConfigurationModel;
|
||||
private _loaded: boolean = false;
|
||||
|
||||
private readonly configurationNames: string[];
|
||||
protected readonly configurationResources: URI[];
|
||||
protected readonly _onDidChange: Emitter<void> = this._register(new Emitter<void>());
|
||||
readonly onDidChange: Event<void> = this._onDidChange.event;
|
||||
|
||||
constructor(protected readonly folder: URI, workbenchState: WorkbenchState, from?: AbstractFolderConfiguration) {
|
||||
constructor(protected readonly configurationFolder: URI, workbenchState: WorkbenchState, from?: AbstractFolderConfiguration) {
|
||||
super();
|
||||
|
||||
this.configurationNames = [FOLDER_SETTINGS_NAME /*First one should be settings */, TASKS_CONFIGURATION_KEY, LAUNCH_CONFIGURATION_KEY];
|
||||
this.configurationResources = this.configurationNames.map(name => resources.joinPath(this.configurationFolder, `${name}.json`));
|
||||
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();
|
||||
@@ -518,23 +648,37 @@ export abstract class AbstractFolderConfiguration extends Disposable implements
|
||||
return this._loaded;
|
||||
}
|
||||
|
||||
loadConfiguration(): Promise<ConfigurationModel> {
|
||||
return this.loadFolderConfigurationContents()
|
||||
.then((contents) => {
|
||||
async loadConfiguration(): Promise<ConfigurationModel> {
|
||||
const configurationContents = await Promise.all(this.configurationResources.map(resource =>
|
||||
this.loadConfigurationResourceContents(resource)
|
||||
.then(undefined, error => {
|
||||
/* never fail */
|
||||
errors.onUnexpectedError(error);
|
||||
return undefined;
|
||||
})));
|
||||
|
||||
// reset
|
||||
this._standAloneConfigurations = [];
|
||||
this._folderSettingsModelParser.parse('');
|
||||
// reset
|
||||
this._standAloneConfigurations = [];
|
||||
this._folderSettingsModelParser.parse('');
|
||||
|
||||
// parse
|
||||
this.parseContents(contents);
|
||||
// parse
|
||||
if (configurationContents[0]) {
|
||||
this._folderSettingsModelParser.parse(configurationContents[0]);
|
||||
}
|
||||
for (let index = 1; index < configurationContents.length; index++) {
|
||||
const contents = configurationContents[index];
|
||||
if (contents) {
|
||||
const standAloneConfigurationModelParser = new StandaloneConfigurationModelParser(this.configurationResources[index].toString(), this.configurationNames[index]);
|
||||
standAloneConfigurationModelParser.parse(contents);
|
||||
this._standAloneConfigurations.push(standAloneConfigurationModelParser.configurationModel);
|
||||
}
|
||||
}
|
||||
|
||||
// Consolidate (support *.json files in the workspace settings folder)
|
||||
this.consolidate();
|
||||
// Consolidate (support *.json files in the workspace settings folder)
|
||||
this.consolidate();
|
||||
|
||||
this._loaded = true;
|
||||
return this._cache;
|
||||
});
|
||||
this._loaded = true;
|
||||
return this._cache;
|
||||
}
|
||||
|
||||
reprocess(): ConfigurationModel {
|
||||
@@ -550,109 +694,41 @@ export abstract class AbstractFolderConfiguration extends Disposable implements
|
||||
this._cache = this._folderSettingsModelParser.configurationModel.merge(...this._standAloneConfigurations);
|
||||
}
|
||||
|
||||
private parseContents(contents: { resource: URI, value: string }[]): void {
|
||||
for (const content of contents) {
|
||||
if (isFolderSettingsConfigurationFile(content.resource)) {
|
||||
this._folderSettingsModelParser.parse(content.value);
|
||||
} else {
|
||||
const name = resources.basename(content.resource);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract loadFolderConfigurationContents(): Promise<{ resource: URI, value: string }[]>;
|
||||
protected abstract loadConfigurationResourceContents(configurationResource: URI): Promise<string | undefined>;
|
||||
}
|
||||
|
||||
export class NodeBasedFolderConfiguration extends AbstractFolderConfiguration {
|
||||
|
||||
private readonly folderConfigurationPath: URI;
|
||||
|
||||
constructor(folder: URI, configFolderRelativePath: string, workbenchState: WorkbenchState) {
|
||||
super(folder, workbenchState);
|
||||
this.folderConfigurationPath = resources.joinPath(folder, configFolderRelativePath);
|
||||
constructor(private readonly configurationFileService: IConfigurationFileService, configurationFolder: URI, workbenchState: WorkbenchState) {
|
||||
super(configurationFolder, workbenchState);
|
||||
}
|
||||
|
||||
protected loadFolderConfigurationContents(): Promise<{ resource: URI, value: string }[]> {
|
||||
return this.resolveStat(this.folderConfigurationPath).then(stat => {
|
||||
if (!stat.isDirectory || !stat.children) {
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
return this.resolveContents(stat.children.filter(stat => isFolderConfigurationFile(stat.resource))
|
||||
.map(stat => stat.resource));
|
||||
}, err => [] /* never fail this call */)
|
||||
.then(undefined, e => {
|
||||
errors.onUnexpectedError(e);
|
||||
return [];
|
||||
});
|
||||
}
|
||||
|
||||
private resolveContents(resources: URI[]): Promise<{ resource: URI, value: string }[]> {
|
||||
return Promise.all(resources.map(resource =>
|
||||
pfs.readFile(resource.fsPath)
|
||||
.then(contents => ({ resource, value: contents.toString() }))));
|
||||
}
|
||||
|
||||
private resolveStat(resource: URI): Promise<{ resource: URI, isDirectory?: boolean, children?: { resource: URI; }[] }> {
|
||||
return new Promise<{ 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: resources.joinPath(resource, child) }; })
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
protected async loadConfigurationResourceContents(configurationResource: URI): Promise<string | undefined> {
|
||||
const exists = await this.configurationFileService.exists(configurationResource);
|
||||
if (exists) {
|
||||
return this.configurationFileService.resolveContent(configurationResource);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
export class FileServiceBasedFolderConfiguration extends AbstractFolderConfiguration {
|
||||
|
||||
private reloadConfigurationScheduler: RunOnceScheduler;
|
||||
private readonly folderConfigurationPath: URI;
|
||||
private readonly loadConfigurationDelayer = new Delayer<Array<{ resource: URI, value: string }>>(50);
|
||||
private changeEventTriggerScheduler: RunOnceScheduler;
|
||||
|
||||
constructor(folder: URI, private configFolderRelativePath: string, workbenchState: WorkbenchState, private fileService: IFileService, from?: AbstractFolderConfiguration) {
|
||||
super(folder, workbenchState, from);
|
||||
this.folderConfigurationPath = resources.joinPath(folder, configFolderRelativePath);
|
||||
this.reloadConfigurationScheduler = this._register(new RunOnceScheduler(() => this._onDidChange.fire(), 50));
|
||||
constructor(configurationFolder: URI, workbenchState: WorkbenchState, private fileService: IFileService, from?: AbstractFolderConfiguration) {
|
||||
super(configurationFolder, workbenchState, from);
|
||||
this.changeEventTriggerScheduler = this._register(new RunOnceScheduler(() => this._onDidChange.fire(), 50));
|
||||
this._register(fileService.onFileChanges(e => this.handleWorkspaceFileEvents(e)));
|
||||
}
|
||||
|
||||
protected loadFolderConfigurationContents(): Promise<Array<{ resource: URI, value: string }>> {
|
||||
return Promise.resolve(this.loadConfigurationDelayer.trigger(() => this.doLoadFolderConfigurationContents()));
|
||||
}
|
||||
|
||||
private doLoadFolderConfigurationContents(): Promise<Array<{ resource: URI, value: string }>> {
|
||||
const workspaceFilePathToConfiguration: { [relativeWorkspacePath: string]: Promise<IContent | undefined> } = Object.create(null);
|
||||
const bulkContentFetchromise = Promise.resolve(this.fileService.resolve(this.folderConfigurationPath))
|
||||
.then(stat => {
|
||||
if (stat.isDirectory && stat.children) {
|
||||
stat.children
|
||||
.filter(child => isFolderConfigurationFile(child.resource))
|
||||
.forEach(child => {
|
||||
const folderRelativePath = this.toFolderRelativePath(child.resource);
|
||||
if (folderRelativePath) {
|
||||
workspaceFilePathToConfiguration[folderRelativePath] = Promise.resolve(this.fileService.resolveContent(child.resource)).then(undefined, errors.onUnexpectedError);
|
||||
}
|
||||
});
|
||||
}
|
||||
}).then(undefined, err => [] /* never fail this call */);
|
||||
|
||||
return bulkContentFetchromise.then(() => Promise.all<IContent>(collections.values(workspaceFilePathToConfiguration))).then(contents => contents.filter(content => content !== undefined));
|
||||
protected async loadConfigurationResourceContents(configurationResource: URI): Promise<string | undefined> {
|
||||
const exists = await this.fileService.exists(configurationResource);
|
||||
if (exists) {
|
||||
const contents = await this.fileService.resolveContent(configurationResource);
|
||||
return contents.value;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private handleWorkspaceFileEvents(event: FileChangesEvent): void {
|
||||
@@ -664,9 +740,9 @@ export class FileServiceBasedFolderConfiguration extends AbstractFolderConfigura
|
||||
const resource = events[i].resource;
|
||||
const basename = resources.basename(resource);
|
||||
const isJson = extname(basename) === '.json';
|
||||
const isDeletedSettingsFolder = (events[i].type === FileChangeType.DELETED && basename === this.configFolderRelativePath);
|
||||
const isConfigurationFolderDeleted = (events[i].type === FileChangeType.DELETED && resources.isEqual(resource, this.configurationFolder));
|
||||
|
||||
if (!isJson && !isDeletedSettingsFolder) {
|
||||
if (!isJson && !isConfigurationFolderDeleted) {
|
||||
continue; // only JSON files or the actual settings folder
|
||||
}
|
||||
|
||||
@@ -676,72 +752,69 @@ export class FileServiceBasedFolderConfiguration extends AbstractFolderConfigura
|
||||
}
|
||||
|
||||
// Handle case where ".vscode" got deleted
|
||||
if (isDeletedSettingsFolder) {
|
||||
if (isConfigurationFolderDeleted) {
|
||||
affectedByChanges = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// only valid workspace config files
|
||||
if (!isFolderConfigurationFile(resource)) {
|
||||
continue;
|
||||
if (this.configurationResources.some(configurationResource => resources.isEqual(configurationResource, resource))) {
|
||||
affectedByChanges = true;
|
||||
break;
|
||||
}
|
||||
|
||||
affectedByChanges = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (affectedByChanges) {
|
||||
this.reloadConfigurationScheduler.schedule();
|
||||
this.changeEventTriggerScheduler.schedule();
|
||||
}
|
||||
}
|
||||
|
||||
private toFolderRelativePath(resource: URI): string | undefined {
|
||||
if (resources.isEqualOrParent(resource, this.folderConfigurationPath)) {
|
||||
return resources.relativePath(this.folderConfigurationPath, resource);
|
||||
if (resources.isEqualOrParent(resource, this.configurationFolder)) {
|
||||
return resources.relativePath(this.configurationFolder, resource);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
export class CachedFolderConfiguration extends Disposable implements IFolderConfiguration {
|
||||
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;
|
||||
|
||||
private readonly key: Thenable<ConfigurationKey>;
|
||||
loaded: boolean = false;
|
||||
|
||||
constructor(
|
||||
folder: URI,
|
||||
configFolderRelativePath: string,
|
||||
environmentService: IEnvironmentService) {
|
||||
private readonly configurationCache: IConfigurationCache
|
||||
) {
|
||||
super();
|
||||
this.cachedFolderPath = join(environmentService.userDataPath, 'CachedConfigurations', 'folders', createHash('md5').update(join(folder.path, configFolderRelativePath)).digest('hex'));
|
||||
this.cachedConfigurationPath = join(this.cachedFolderPath, 'configuration.json');
|
||||
this.key = createSHA1(join(folder.path, configFolderRelativePath)).then(key => (<ConfigurationKey>{ type: 'folder', key }));
|
||||
this.configurationModel = new ConfigurationModel();
|
||||
}
|
||||
|
||||
loadConfiguration(): Promise<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);
|
||||
async loadConfiguration(): Promise<ConfigurationModel> {
|
||||
try {
|
||||
const key = await this.key;
|
||||
const contents = await this.configurationCache.read(key);
|
||||
const parsed: IConfigurationModel = JSON.parse(contents.toString());
|
||||
this.configurationModel = new ConfigurationModel(parsed.contents, parsed.keys, parsed.overrides);
|
||||
this.loaded = true;
|
||||
} catch (e) {
|
||||
}
|
||||
return this.configurationModel;
|
||||
}
|
||||
|
||||
updateConfiguration(configurationModel: ConfigurationModel): Promise<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 undefined;
|
||||
});
|
||||
async updateConfiguration(configurationModel: ConfigurationModel): Promise<void> {
|
||||
const key = await this.key;
|
||||
if (configurationModel.keys.length) {
|
||||
await this.configurationCache.write(key, JSON.stringify(configurationModel.toJSON()));
|
||||
} else {
|
||||
await this.configurationCache.remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
reprocess(): ConfigurationModel {
|
||||
@@ -751,12 +824,6 @@ export class CachedFolderConfiguration extends Disposable implements IFolderConf
|
||||
getUnsupportedKeys(): string[] {
|
||||
return [];
|
||||
}
|
||||
|
||||
private createCachedFolder(): Promise<boolean> {
|
||||
return Promise.resolve(pfs.exists(this.cachedFolderPath))
|
||||
.then(undefined, () => false)
|
||||
.then(exists => exists ? exists : pfs.mkdirp(this.cachedFolderPath).then(() => true, () => false));
|
||||
}
|
||||
}
|
||||
|
||||
export class FolderConfiguration extends Disposable implements IFolderConfiguration {
|
||||
@@ -765,24 +832,27 @@ export class FolderConfiguration extends Disposable implements IFolderConfigurat
|
||||
readonly onDidChange: Event<void> = this._onDidChange.event;
|
||||
|
||||
private folderConfiguration: IFolderConfiguration;
|
||||
private readonly configurationFolder: URI;
|
||||
private cachedFolderConfiguration: CachedFolderConfiguration;
|
||||
private _loaded: boolean = false;
|
||||
|
||||
constructor(
|
||||
readonly workspaceFolder: IWorkspaceFolder,
|
||||
private readonly configFolderRelativePath: string,
|
||||
configFolderRelativePath: string,
|
||||
private readonly workbenchState: WorkbenchState,
|
||||
private environmentService: IEnvironmentService,
|
||||
configurationFileService: IConfigurationFileService,
|
||||
configurationCache: IConfigurationCache,
|
||||
fileService?: IFileService
|
||||
) {
|
||||
super();
|
||||
|
||||
this.cachedFolderConfiguration = new CachedFolderConfiguration(this.workspaceFolder.uri, this.configFolderRelativePath, this.environmentService);
|
||||
this.configurationFolder = resources.joinPath(workspaceFolder.uri, configFolderRelativePath);
|
||||
this.cachedFolderConfiguration = new CachedFolderConfiguration(workspaceFolder.uri, configFolderRelativePath, configurationCache);
|
||||
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.folderConfiguration = new FileServiceBasedFolderConfiguration(this.configurationFolder, this.workbenchState, fileService);
|
||||
} else if (workspaceFolder.uri.scheme === Schemas.file) {
|
||||
this.folderConfiguration = new NodeBasedFolderConfiguration(configurationFileService, this.configurationFolder, this.workbenchState);
|
||||
}
|
||||
this._register(this.folderConfiguration.onDidChange(e => this.onDidFolderConfigurationChange()));
|
||||
}
|
||||
@@ -817,7 +887,7 @@ export class FolderConfiguration extends Disposable implements IFolderConfigurat
|
||||
}
|
||||
|
||||
private adoptFromCachedConfiguration(fileService: IFileService): Promise<boolean> {
|
||||
const folderConfiguration = new FileServiceBasedFolderConfiguration(this.workspaceFolder.uri, this.configFolderRelativePath, this.workbenchState, fileService);
|
||||
const folderConfiguration = new FileServiceBasedFolderConfiguration(this.configurationFolder, this.workbenchState, fileService);
|
||||
return folderConfiguration.loadConfiguration()
|
||||
.then(() => {
|
||||
this.folderConfiguration = folderConfiguration;
|
||||
@@ -829,7 +899,7 @@ export class FolderConfiguration extends Disposable implements IFolderConfigurat
|
||||
|
||||
private adoptFromNodeBasedConfiguration(fileService: IFileService): Promise<boolean> {
|
||||
const oldFolderConfiguration = this.folderConfiguration;
|
||||
this.folderConfiguration = new FileServiceBasedFolderConfiguration(this.workspaceFolder.uri, this.configFolderRelativePath, this.workbenchState, fileService, <AbstractFolderConfiguration>oldFolderConfiguration);
|
||||
this.folderConfiguration = new FileServiceBasedFolderConfiguration(this.configurationFolder, this.workbenchState, fileService, <AbstractFolderConfiguration>oldFolderConfiguration);
|
||||
oldFolderConfiguration.dispose();
|
||||
this._register(this.folderConfiguration.onDidChange(e => this.onDidFolderConfigurationChange()));
|
||||
return Promise.resolve(false);
|
||||
@@ -841,7 +911,7 @@ export class FolderConfiguration extends Disposable implements IFolderConfigurat
|
||||
}
|
||||
|
||||
private updateCache(): Promise<void> {
|
||||
if (this.workspaceFolder.uri.scheme !== Schemas.file && this.folderConfiguration instanceof FileServiceBasedFolderConfiguration) {
|
||||
if (this.configurationFolder.scheme !== Schemas.file && this.folderConfiguration instanceof FileServiceBasedFolderConfiguration) {
|
||||
return this.folderConfiguration.loadConfiguration()
|
||||
.then(configurationModel => this.cachedFolderConfiguration.updateConfiguration(configurationModel));
|
||||
}
|
||||
@@ -10,25 +10,20 @@ import { ResourceMap } from 'vs/base/common/map';
|
||||
import { equals, deepClone } from 'vs/base/common/objects';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { Queue, Barrier } from 'vs/base/common/async';
|
||||
import { 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 { isLinux } from 'vs/base/common/platform';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { ConfigurationChangeEvent, ConfigurationModel, DefaultConfigurationModel } from 'vs/platform/configuration/common/configurationModels';
|
||||
import { IConfigurationChangeEvent, ConfigurationTarget, IConfigurationOverrides, keyFromOverrideIdentifier, isConfigurationOverrides, IConfigurationData, IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { Configuration, WorkspaceConfigurationChangeEvent, AllKeysConfigurationChangeEvent } from 'vs/workbench/services/configuration/common/configurationModels';
|
||||
import { FOLDER_CONFIG_FOLDER_NAME, defaultSettingsSchemaId, userSettingsSchemaId, workspaceSettingsSchemaId, folderSettingsSchemaId } from 'vs/workbench/services/configuration/common/configuration';
|
||||
import { FOLDER_CONFIG_FOLDER_NAME, defaultSettingsSchemaId, userSettingsSchemaId, workspaceSettingsSchemaId, folderSettingsSchemaId, IConfigurationCache, IConfigurationFileService } from 'vs/workbench/services/configuration/common/configuration';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { IConfigurationNode, IConfigurationRegistry, Extensions, IConfigurationPropertySchema, allSettings, windowSettings, resourceSettings, applicationSettings } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
import { IConfigurationRegistry, Extensions, allSettings, windowSettings, resourceSettings, applicationSettings } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
import { IWorkspaceIdentifier, isWorkspaceIdentifier, IStoredWorkspaceFolder, isStoredWorkspaceFolder, IWorkspaceFolderCreationData, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, IWorkspaceInitializationPayload, isSingleFolderWorkspaceInitializationPayload, ISingleFolderWorkspaceInitializationPayload, IEmptyWorkspaceInitializationPayload, useSlashForPath, getStoredWorkspaceFolder } from 'vs/platform/workspaces/common/workspaces';
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import product from 'vs/platform/product/node/product';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ConfigurationEditingService } from 'vs/workbench/services/configuration/common/configurationEditingService';
|
||||
import { WorkspaceConfiguration, FolderConfiguration, RemoteUserConfiguration, LocalUserConfiguration } from 'vs/workbench/services/configuration/node/configuration';
|
||||
import { WorkspaceConfiguration, FolderConfiguration, RemoteUserConfiguration, LocalUserConfiguration } from 'vs/workbench/services/configuration/browser/configuration';
|
||||
import { JSONEditingService } from 'vs/workbench/services/configuration/common/jsonEditingService';
|
||||
import { IJSONSchema, IJSONSchemaMap } from 'vs/base/common/jsonSchema';
|
||||
import { localize } from 'vs/nls';
|
||||
@@ -36,7 +31,6 @@ import { isEqual, dirname } from 'vs/base/common/resources';
|
||||
import { mark } from 'vs/base/common/performance';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
|
||||
import { IWindowConfiguration } from 'vs/platform/windows/common/windows';
|
||||
|
||||
export class WorkspaceService extends Disposable implements IConfigurationService, IWorkspaceContextService {
|
||||
|
||||
@@ -44,9 +38,10 @@ export class WorkspaceService extends Disposable implements IConfigurationServic
|
||||
|
||||
private workspace: Workspace;
|
||||
private completeWorkspaceBarrier: Barrier;
|
||||
private readonly configurationCache: IConfigurationCache;
|
||||
private _configuration: Configuration;
|
||||
private defaultConfiguration: DefaultConfigurationModel;
|
||||
private localUserConfiguration: LocalUserConfiguration;
|
||||
private localUserConfiguration: LocalUserConfiguration | null = null;
|
||||
private remoteUserConfiguration: RemoteUserConfiguration | null = null;
|
||||
private workspaceConfiguration: WorkspaceConfiguration;
|
||||
private cachedFolderConfigs: ResourceMap<FolderConfiguration>;
|
||||
@@ -69,18 +64,25 @@ export class WorkspaceService extends Disposable implements IConfigurationServic
|
||||
private configurationEditingService: ConfigurationEditingService;
|
||||
private jsonEditingService: JSONEditingService;
|
||||
|
||||
constructor(configuration: IWindowConfiguration, private environmentService: IEnvironmentService, private remoteAgentService: IRemoteAgentService, private workspaceSettingsRootFolder: string = FOLDER_CONFIG_FOLDER_NAME) {
|
||||
constructor(
|
||||
{ userSettingsResource, remoteAuthority, configurationCache }: { userSettingsResource?: URI, remoteAuthority?: string, configurationCache: IConfigurationCache },
|
||||
private readonly configurationFileService: IConfigurationFileService,
|
||||
private readonly remoteAgentService: IRemoteAgentService,
|
||||
) {
|
||||
super();
|
||||
|
||||
this.completeWorkspaceBarrier = new Barrier();
|
||||
this.defaultConfiguration = new DefaultConfigurationModel();
|
||||
this.localUserConfiguration = this._register(new LocalUserConfiguration(environmentService));
|
||||
this._register(this.localUserConfiguration.onDidChangeConfiguration(userConfiguration => this.onLocalUserConfigurationChanged(userConfiguration)));
|
||||
if (configuration.remoteAuthority) {
|
||||
this.remoteUserConfiguration = this._register(new RemoteUserConfiguration(configuration.remoteAuthority, environmentService));
|
||||
this.configurationCache = configurationCache;
|
||||
if (userSettingsResource) {
|
||||
this.localUserConfiguration = this._register(new LocalUserConfiguration(userSettingsResource, configurationFileService));
|
||||
this._register(this.localUserConfiguration.onDidChangeConfiguration(userConfiguration => this.onLocalUserConfigurationChanged(userConfiguration)));
|
||||
}
|
||||
if (remoteAuthority) {
|
||||
this.remoteUserConfiguration = this._register(new RemoteUserConfiguration(remoteAuthority, configurationCache));
|
||||
this._register(this.remoteUserConfiguration.onDidChangeConfiguration(userConfiguration => this.onRemoteUserConfigurationChanged(userConfiguration)));
|
||||
}
|
||||
this.workspaceConfiguration = this._register(new WorkspaceConfiguration(environmentService));
|
||||
this.workspaceConfiguration = this._register(new WorkspaceConfiguration(configurationCache, this.configurationFileService));
|
||||
this._register(this.workspaceConfiguration.onDidUpdateConfiguration(() => this.onWorkspaceConfigurationChanged()));
|
||||
|
||||
this._register(Registry.as<IConfigurationRegistry>(Extensions.Configuration).onDidSchemaChange(e => this.registerConfigurationSchemas()));
|
||||
@@ -284,10 +286,10 @@ export class WorkspaceService extends Disposable implements IConfigurationServic
|
||||
return this._configuration.keys();
|
||||
}
|
||||
|
||||
initialize(arg: IWorkspaceInitializationPayload, postInitialisationTask: () => void = () => null): Promise<any> {
|
||||
initialize(arg: IWorkspaceInitializationPayload): Promise<any> {
|
||||
mark('willInitWorkspaceService');
|
||||
return this.createWorkspace(arg)
|
||||
.then(workspace => this.updateWorkspaceAndInitializeConfiguration(workspace, postInitialisationTask)).then(() => {
|
||||
.then(workspace => this.updateWorkspaceAndInitializeConfiguration(workspace)).then(() => {
|
||||
mark('didInitWorkspaceService');
|
||||
});
|
||||
}
|
||||
@@ -295,7 +297,14 @@ export class WorkspaceService extends Disposable implements IConfigurationServic
|
||||
acquireFileService(fileService: IFileService): void {
|
||||
this.fileService = fileService;
|
||||
const changedWorkspaceFolders: IWorkspaceFolder[] = [];
|
||||
this.localUserConfiguration.adopt(fileService);
|
||||
if (this.localUserConfiguration) {
|
||||
this.localUserConfiguration.adopt(fileService)
|
||||
.then(changedModel => {
|
||||
if (changedModel) {
|
||||
this.onLocalUserConfigurationChanged(changedModel);
|
||||
}
|
||||
});
|
||||
}
|
||||
Promise.all([this.workspaceConfiguration.adopt(fileService), ...this.cachedFolderConfigs.values()
|
||||
.map(folderConfiguration => folderConfiguration.adopt(fileService)
|
||||
.then(result => {
|
||||
@@ -382,7 +391,7 @@ export class WorkspaceService extends Disposable implements IConfigurationServic
|
||||
}
|
||||
}
|
||||
|
||||
private updateWorkspaceAndInitializeConfiguration(workspace: Workspace, postInitialisationTask: () => void): Promise<void> {
|
||||
private updateWorkspaceAndInitializeConfiguration(workspace: Workspace): Promise<void> {
|
||||
const hasWorkspaceBefore = !!this.workspace;
|
||||
let previousState: WorkbenchState;
|
||||
let previousWorkspacePath: string | undefined;
|
||||
@@ -399,8 +408,6 @@ export class WorkspaceService extends Disposable implements IConfigurationServic
|
||||
|
||||
return this.initializeConfiguration().then(() => {
|
||||
|
||||
postInitialisationTask(); // Post initialisation task should be run before triggering events.
|
||||
|
||||
// Trigger changes after configuration initialization so that configuration is up to date.
|
||||
if (hasWorkspaceBefore) {
|
||||
const newState = this.getWorkbenchState();
|
||||
@@ -446,12 +453,12 @@ export class WorkspaceService extends Disposable implements IConfigurationServic
|
||||
}
|
||||
|
||||
private initializeUserConfiguration(): Promise<{ local: ConfigurationModel, remote: ConfigurationModel }> {
|
||||
return Promise.all([this.localUserConfiguration.initialize(), this.remoteUserConfiguration ? this.remoteUserConfiguration.initialize() : Promise.resolve(new ConfigurationModel())])
|
||||
return Promise.all([this.localUserConfiguration ? this.localUserConfiguration.initialize() : Promise.resolve(new ConfigurationModel()), this.remoteUserConfiguration ? this.remoteUserConfiguration.initialize() : Promise.resolve(new ConfigurationModel())])
|
||||
.then(([local, remote]) => ({ local, remote }));
|
||||
}
|
||||
|
||||
private reloadUserConfiguration(key?: string): Promise<{ local: ConfigurationModel, remote: ConfigurationModel }> {
|
||||
return Promise.all([this.localUserConfiguration.reload(), this.remoteUserConfiguration ? this.remoteUserConfiguration.reload() : Promise.resolve(new ConfigurationModel())])
|
||||
return Promise.all([this.localUserConfiguration ? this.localUserConfiguration.reload() : Promise.resolve(new ConfigurationModel()), this.remoteUserConfiguration ? this.remoteUserConfiguration.reload() : Promise.resolve(new ConfigurationModel())])
|
||||
.then(([local, remote]) => ({ local, remote }));
|
||||
}
|
||||
|
||||
@@ -622,7 +629,7 @@ export class WorkspaceService extends Disposable implements IConfigurationServic
|
||||
return Promise.all([...folders.map(folder => {
|
||||
let folderConfiguration = this.cachedFolderConfigs.get(folder.uri);
|
||||
if (!folderConfiguration) {
|
||||
folderConfiguration = new FolderConfiguration(folder, this.workspaceSettingsRootFolder, this.getWorkbenchState(), this.environmentService, this.fileService);
|
||||
folderConfiguration = new FolderConfiguration(folder, FOLDER_CONFIG_FOLDER_NAME, this.getWorkbenchState(), this.configurationFileService, this.configurationCache, this.fileService);
|
||||
this._register(folderConfiguration.onDidChange(() => this.onWorkspaceFolderConfigurationChanged(folder)));
|
||||
this.cachedFolderConfigs.set(folder.uri, this._register(folderConfiguration));
|
||||
}
|
||||
@@ -709,99 +716,4 @@ export class WorkspaceService extends Disposable implements IConfigurationServic
|
||||
}
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
interface IExportedConfigurationNode {
|
||||
name: string;
|
||||
description: string;
|
||||
default: any;
|
||||
type?: string | string[];
|
||||
enum?: any[];
|
||||
enumDescriptions?: string[];
|
||||
}
|
||||
|
||||
interface IConfigurationExport {
|
||||
settings: IExportedConfigurationNode[];
|
||||
buildTime: number;
|
||||
commit?: string;
|
||||
buildNumber?: number;
|
||||
}
|
||||
|
||||
export class DefaultConfigurationExportHelper {
|
||||
|
||||
constructor(
|
||||
@IEnvironmentService environmentService: IEnvironmentService,
|
||||
@IExtensionService private readonly extensionService: IExtensionService,
|
||||
@ICommandService private readonly commandService: ICommandService) {
|
||||
if (environmentService.args['export-default-configuration']) {
|
||||
this.writeConfigModelAndQuit(environmentService.args['export-default-configuration']);
|
||||
}
|
||||
}
|
||||
|
||||
private writeConfigModelAndQuit(targetPath: string): Promise<void> {
|
||||
return Promise.resolve(this.extensionService.whenInstalledExtensionsRegistered())
|
||||
.then(() => this.writeConfigModel(targetPath))
|
||||
.then(() => this.commandService.executeCommand('workbench.action.quit'))
|
||||
.then(() => { });
|
||||
}
|
||||
|
||||
private writeConfigModel(targetPath: string): Promise<void> {
|
||||
const config = this.getConfigModel();
|
||||
|
||||
const resultString = JSON.stringify(config, undefined, ' ');
|
||||
return writeFile(targetPath, resultString);
|
||||
}
|
||||
|
||||
private getConfigModel(): IConfigurationExport {
|
||||
const configRegistry = Registry.as<IConfigurationRegistry>(Extensions.Configuration);
|
||||
const configurations = configRegistry.getConfigurations().slice();
|
||||
const settings: IExportedConfigurationNode[] = [];
|
||||
|
||||
const processProperty = (name: string, prop: IConfigurationPropertySchema) => {
|
||||
const propDetails: IExportedConfigurationNode = {
|
||||
name,
|
||||
description: prop.description || prop.markdownDescription || '',
|
||||
default: prop.default,
|
||||
type: prop.type
|
||||
};
|
||||
|
||||
if (prop.enum) {
|
||||
propDetails.enum = prop.enum;
|
||||
}
|
||||
|
||||
if (prop.enumDescriptions || prop.markdownEnumDescriptions) {
|
||||
propDetails.enumDescriptions = prop.enumDescriptions || prop.markdownEnumDescriptions;
|
||||
}
|
||||
|
||||
settings.push(propDetails);
|
||||
};
|
||||
|
||||
const processConfig = (config: IConfigurationNode) => {
|
||||
if (config.properties) {
|
||||
for (let name in config.properties) {
|
||||
processProperty(name, config.properties[name]);
|
||||
}
|
||||
}
|
||||
|
||||
if (config.allOf) {
|
||||
config.allOf.forEach(processConfig);
|
||||
}
|
||||
};
|
||||
|
||||
configurations.forEach(processConfig);
|
||||
|
||||
const excludedProps = configRegistry.getExcludedConfigurationProperties();
|
||||
for (let name in excludedProps) {
|
||||
processProperty(name, excludedProps[name]);
|
||||
}
|
||||
|
||||
const result: IConfigurationExport = {
|
||||
settings: settings.sort((a, b) => a.name.localeCompare(b.name)),
|
||||
buildTime: Date.now(),
|
||||
commit: product.commit,
|
||||
buildNumber: product.settingsSearchBuildId
|
||||
};
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,8 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
|
||||
export const FOLDER_CONFIG_FOLDER_NAME = '.azuredatastudio';
|
||||
export const FOLDER_SETTINGS_NAME = 'settings';
|
||||
export const FOLDER_SETTINGS_PATH = `${FOLDER_CONFIG_FOLDER_NAME}/${FOLDER_SETTINGS_NAME}.json`;
|
||||
@@ -19,3 +21,19 @@ 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_CONFIGURATION_KEY}.json`;
|
||||
WORKSPACE_STANDALONE_CONFIGURATIONS[LAUNCH_CONFIGURATION_KEY] = `${FOLDER_CONFIG_FOLDER_NAME}/${LAUNCH_CONFIGURATION_KEY}.json`;
|
||||
|
||||
|
||||
export type ConfigurationKey = { type: 'user' | 'workspaces' | 'folder', key: string };
|
||||
|
||||
export interface IConfigurationCache {
|
||||
|
||||
read(key: ConfigurationKey): Promise<string>;
|
||||
write(key: ConfigurationKey, content: string): Promise<void>;
|
||||
remove(key: ConfigurationKey): Promise<void>;
|
||||
|
||||
}
|
||||
|
||||
export interface IConfigurationFileService {
|
||||
exists(resource: URI): Promise<boolean>;
|
||||
resolveContent(resource: URI): Promise<string>;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as pfs from 'vs/base/node/pfs';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { join } from 'vs/base/common/path';
|
||||
import { IConfigurationCache, ConfigurationKey } from 'vs/workbench/services/configuration/common/configuration';
|
||||
|
||||
export class ConfigurationCache implements IConfigurationCache {
|
||||
|
||||
private readonly cachedConfigurations: Map<string, CachedConfiguration> = new Map<string, CachedConfiguration>();
|
||||
|
||||
constructor(private readonly environmentService: IEnvironmentService) {
|
||||
}
|
||||
|
||||
read(key: ConfigurationKey): Promise<string> {
|
||||
return this.getCachedConfiguration(key).read();
|
||||
}
|
||||
|
||||
write(key: ConfigurationKey, content: string): Promise<void> {
|
||||
return this.getCachedConfiguration(key).save(content);
|
||||
}
|
||||
|
||||
remove(key: ConfigurationKey): Promise<void> {
|
||||
return this.getCachedConfiguration(key).remove();
|
||||
}
|
||||
|
||||
private getCachedConfiguration({ type, key }: ConfigurationKey): CachedConfiguration {
|
||||
const k = `${type}:${key}`;
|
||||
let cachedConfiguration = this.cachedConfigurations.get(k);
|
||||
if (!cachedConfiguration) {
|
||||
cachedConfiguration = new CachedConfiguration({ type, key }, this.environmentService);
|
||||
this.cachedConfigurations.set(k, cachedConfiguration);
|
||||
}
|
||||
return cachedConfiguration;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
class CachedConfiguration {
|
||||
|
||||
private cachedConfigurationFolderPath: string;
|
||||
private cachedConfigurationFilePath: string;
|
||||
|
||||
constructor(
|
||||
{ type, key }: ConfigurationKey,
|
||||
environmentService: IEnvironmentService
|
||||
) {
|
||||
this.cachedConfigurationFolderPath = join(environmentService.userDataPath, 'CachedConfigurations', type, key);
|
||||
this.cachedConfigurationFilePath = join(this.cachedConfigurationFolderPath, type === 'workspaces' ? 'workspace.json' : 'configuration.json');
|
||||
}
|
||||
|
||||
async read(): Promise<string> {
|
||||
try {
|
||||
const content = await pfs.readFile(this.cachedConfigurationFilePath);
|
||||
return content.toString();
|
||||
} catch (e) {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
async save(content: string): Promise<void> {
|
||||
const created = await this.createCachedFolder();
|
||||
if (created) {
|
||||
await pfs.writeFile(this.cachedConfigurationFilePath, content);
|
||||
}
|
||||
}
|
||||
|
||||
remove(): Promise<void> {
|
||||
return pfs.rimraf(this.cachedConfigurationFolderPath);
|
||||
}
|
||||
|
||||
private createCachedFolder(): Promise<boolean> {
|
||||
return Promise.resolve(pfs.exists(this.cachedConfigurationFolderPath))
|
||||
.then(undefined, () => false)
|
||||
.then(exists => exists ? exists : pfs.mkdirp(this.cachedConfigurationFolderPath).then(() => true, () => false));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { writeFile } from 'vs/base/node/pfs';
|
||||
import product from 'vs/platform/product/node/product';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { IConfigurationNode, IConfigurationRegistry, Extensions, IConfigurationPropertySchema } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
|
||||
interface IExportedConfigurationNode {
|
||||
name: string;
|
||||
description: string;
|
||||
default: any;
|
||||
type?: string | string[];
|
||||
enum?: any[];
|
||||
enumDescriptions?: string[];
|
||||
}
|
||||
|
||||
interface IConfigurationExport {
|
||||
settings: IExportedConfigurationNode[];
|
||||
buildTime: number;
|
||||
commit?: string;
|
||||
buildNumber?: number;
|
||||
}
|
||||
|
||||
export class DefaultConfigurationExportHelper {
|
||||
|
||||
constructor(
|
||||
@IEnvironmentService environmentService: IEnvironmentService,
|
||||
@IExtensionService private readonly extensionService: IExtensionService,
|
||||
@ICommandService private readonly commandService: ICommandService) {
|
||||
if (environmentService.args['export-default-configuration']) {
|
||||
this.writeConfigModelAndQuit(environmentService.args['export-default-configuration']);
|
||||
}
|
||||
}
|
||||
|
||||
private writeConfigModelAndQuit(targetPath: string): Promise<void> {
|
||||
return Promise.resolve(this.extensionService.whenInstalledExtensionsRegistered())
|
||||
.then(() => this.writeConfigModel(targetPath))
|
||||
.then(() => this.commandService.executeCommand('workbench.action.quit'))
|
||||
.then(() => { });
|
||||
}
|
||||
|
||||
private writeConfigModel(targetPath: string): Promise<void> {
|
||||
const config = this.getConfigModel();
|
||||
|
||||
const resultString = JSON.stringify(config, undefined, ' ');
|
||||
return writeFile(targetPath, resultString);
|
||||
}
|
||||
|
||||
private getConfigModel(): IConfigurationExport {
|
||||
const configRegistry = Registry.as<IConfigurationRegistry>(Extensions.Configuration);
|
||||
const configurations = configRegistry.getConfigurations().slice();
|
||||
const settings: IExportedConfigurationNode[] = [];
|
||||
|
||||
const processProperty = (name: string, prop: IConfigurationPropertySchema) => {
|
||||
const propDetails: IExportedConfigurationNode = {
|
||||
name,
|
||||
description: prop.description || prop.markdownDescription || '',
|
||||
default: prop.default,
|
||||
type: prop.type
|
||||
};
|
||||
|
||||
if (prop.enum) {
|
||||
propDetails.enum = prop.enum;
|
||||
}
|
||||
|
||||
if (prop.enumDescriptions || prop.markdownEnumDescriptions) {
|
||||
propDetails.enumDescriptions = prop.enumDescriptions || prop.markdownEnumDescriptions;
|
||||
}
|
||||
|
||||
settings.push(propDetails);
|
||||
};
|
||||
|
||||
const processConfig = (config: IConfigurationNode) => {
|
||||
if (config.properties) {
|
||||
for (let name in config.properties) {
|
||||
processProperty(name, config.properties[name]);
|
||||
}
|
||||
}
|
||||
|
||||
if (config.allOf) {
|
||||
config.allOf.forEach(processConfig);
|
||||
}
|
||||
};
|
||||
|
||||
configurations.forEach(processConfig);
|
||||
|
||||
const excludedProps = configRegistry.getExcludedConfigurationProperties();
|
||||
for (let name in excludedProps) {
|
||||
processProperty(name, excludedProps[name]);
|
||||
}
|
||||
|
||||
const result: IConfigurationExport = {
|
||||
settings: settings.sort((a, b) => a.name.localeCompare(b.name)),
|
||||
buildTime: Date.now(),
|
||||
commit: product.commit,
|
||||
buildNumber: product.settingsSearchBuildId
|
||||
};
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as pfs from 'vs/base/node/pfs';
|
||||
import { IConfigurationFileService } from 'vs/workbench/services/configuration/common/configuration';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
|
||||
export class ConfigurationFileService implements IConfigurationFileService {
|
||||
|
||||
exists(resource: URI): Promise<boolean> {
|
||||
return pfs.exists(resource.fsPath);
|
||||
}
|
||||
|
||||
async resolveContent(resource: URI): Promise<string> {
|
||||
const contents = await pfs.readFile(resource.fsPath);
|
||||
return contents.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -14,31 +14,33 @@ 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 * 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 { TestTextFileService, TestTextResourceConfigurationService, workbenchInstantiationService, TestEnvironmentService } from 'vs/workbench/test/workbenchTestServices';
|
||||
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 { WorkspaceService } from 'vs/workbench/services/configuration/browser/configurationService';
|
||||
import { LegacyFileService } from 'vs/workbench/services/files/node/fileService';
|
||||
import { ConfigurationEditingService, ConfigurationEditingError, ConfigurationEditingErrorCode } from 'vs/workbench/services/configuration/common/configurationEditingService';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { WORKSPACE_STANDALONE_CONFIGURATIONS } from 'vs/workbench/services/configuration/common/configuration';
|
||||
import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
|
||||
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';
|
||||
import { TextModelResolverService } from 'vs/workbench/services/textmodelResolver/common/textModelResolverService';
|
||||
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
|
||||
import { mkdirp } from 'vs/base/node/pfs';
|
||||
import { mkdirp, rimraf, RimRafMode } from 'vs/base/node/pfs';
|
||||
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';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { createHash } from 'crypto';
|
||||
import { IWindowConfiguration } from 'vs/platform/windows/common/windows';
|
||||
import { RemoteAgentService } from 'vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl';
|
||||
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
|
||||
import { FileService2 } from 'vs/workbench/services/files2/common/fileService2';
|
||||
import { NullLogService } from 'vs/platform/log/common/log';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { DiskFileSystemProvider } from 'vs/workbench/services/files2/node/diskFileSystemProvider';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { ConfigurationCache } from 'vs/workbench/services/configuration/node/configurationCache';
|
||||
import { ConfigurationFileService } from 'vs/workbench/services/configuration/node/configurationFileService';
|
||||
|
||||
class SettingsTestEnvironmentService extends EnvironmentService {
|
||||
|
||||
@@ -85,7 +87,7 @@ suite('ConfigurationEditingService', () => {
|
||||
.then(() => setUpServices());
|
||||
});
|
||||
|
||||
async function setUpWorkspace(): Promise<boolean> {
|
||||
async function setUpWorkspace(): Promise<void> {
|
||||
const id = uuid.generateUuid();
|
||||
parentDir = path.join(os.tmpdir(), 'vsctests', id);
|
||||
workspaceDir = path.join(parentDir, 'workspaceconfig', id);
|
||||
@@ -105,11 +107,19 @@ suite('ConfigurationEditingService', () => {
|
||||
instantiationService.stub(IEnvironmentService, environmentService);
|
||||
const remoteAgentService = instantiationService.createInstance(RemoteAgentService, {});
|
||||
instantiationService.stub(IRemoteAgentService, remoteAgentService);
|
||||
const workspaceService = new WorkspaceService(<IWindowConfiguration>{}, environmentService, remoteAgentService);
|
||||
const workspaceService = new WorkspaceService({ userSettingsResource: URI.file(environmentService.appSettingsPath), configurationCache: new ConfigurationCache(environmentService) }, new ConfigurationFileService(), remoteAgentService);
|
||||
instantiationService.stub(IWorkspaceContextService, workspaceService);
|
||||
return workspaceService.initialize(noWorkspace ? { id: '' } : { folder: URI.file(workspaceDir), id: createHash('md5').update(URI.file(workspaceDir).toString()).digest('hex') }).then(() => {
|
||||
instantiationService.stub(IConfigurationService, workspaceService);
|
||||
instantiationService.stub(IFileService, new FileService(workspaceService, TestEnvironmentService, new TestTextResourceConfigurationService(), new TestConfigurationService(), new TestLifecycleService(), new TestStorageService(), new TestNotificationService(), { disableWatcher: true }));
|
||||
const fileService = new FileService2(new NullLogService());
|
||||
fileService.registerProvider(Schemas.file, new DiskFileSystemProvider(new NullLogService()));
|
||||
fileService.setLegacyService(new LegacyFileService(
|
||||
fileService,
|
||||
workspaceService,
|
||||
TestEnvironmentService,
|
||||
new TestTextResourceConfigurationService(),
|
||||
));
|
||||
instantiationService.stub(IFileService, fileService);
|
||||
instantiationService.stub(ITextFileService, instantiationService.createInstance(TestTextFileService));
|
||||
instantiationService.stub(ITextModelService, <ITextModelService>instantiationService.createInstance(TextModelResolverService));
|
||||
instantiationService.stub(ICommandService, CommandService);
|
||||
@@ -135,7 +145,7 @@ suite('ConfigurationEditingService', () => {
|
||||
function clearWorkspace(): Promise<void> {
|
||||
return new Promise<void>((c, e) => {
|
||||
if (parentDir) {
|
||||
extfs.del(parentDir, os.tmpdir(), () => c(undefined), () => c(undefined));
|
||||
rimraf(parentDir, RimRafMode.MOVE).then(c, c);
|
||||
} else {
|
||||
c(undefined);
|
||||
}
|
||||
|
||||
@@ -16,15 +16,14 @@ import { parseArgs } from 'vs/platform/environment/node/argv';
|
||||
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 { ISingleFolderWorkspaceInitializationPayload } from 'vs/platform/workspaces/common/workspaces';
|
||||
import { WorkspaceService } from 'vs/workbench/services/configuration/browser/configurationService';
|
||||
import { ISingleFolderWorkspaceInitializationPayload, IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
|
||||
import { ConfigurationEditingErrorCode } from 'vs/workbench/services/configuration/common/configurationEditingService';
|
||||
import { IFileService, FileChangesEvent, FileChangeType } 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, TestStorageService } from 'vs/workbench/test/workbenchTestServices';
|
||||
import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService';
|
||||
import { FileService } from 'vs/workbench/services/files/node/fileService';
|
||||
import { workbenchInstantiationService, TestTextResourceConfigurationService, TestTextFileService, TestEnvironmentService } from 'vs/workbench/test/workbenchTestServices';
|
||||
import { LegacyFileService } from 'vs/workbench/services/files/node/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';
|
||||
@@ -32,15 +31,18 @@ import { TextModelResolverService } from 'vs/workbench/services/textmodelResolve
|
||||
import { IJSONEditingService } from 'vs/workbench/services/configuration/common/jsonEditing';
|
||||
import { JSONEditingService } from 'vs/workbench/services/configuration/common/jsonEditingService';
|
||||
import { createHash } from 'crypto';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { originalFSPath } from 'vs/base/common/resources';
|
||||
import { isLinux } from 'vs/base/common/platform';
|
||||
import { IWorkspaceIdentifier } from 'vs/workbench/services/configuration/node/configuration';
|
||||
import { IWindowConfiguration } from 'vs/platform/windows/common/windows';
|
||||
import { RemoteAgentService } from 'vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl';
|
||||
import { RemoteAuthorityResolverService } from 'vs/platform/remote/electron-browser/remoteAuthorityResolverService';
|
||||
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
|
||||
import { FileService2 } from 'vs/workbench/services/files2/common/fileService2';
|
||||
import { NullLogService } from 'vs/platform/log/common/log';
|
||||
import { DiskFileSystemProvider } from 'vs/workbench/services/files2/node/diskFileSystemProvider';
|
||||
import { ConfigurationCache } from 'vs/workbench/services/configuration/node/configurationCache';
|
||||
import { ConfigurationFileService } from 'vs/workbench/services/configuration/node/configurationFileService';
|
||||
|
||||
class SettingsTestEnvironmentService extends EnvironmentService {
|
||||
|
||||
@@ -91,7 +93,7 @@ function setUpWorkspace(folders: string[]): Promise<{ parentDir: string, configP
|
||||
|
||||
suite('WorkspaceContextService - Folder', () => {
|
||||
test('getWorkspace()', () => {
|
||||
// {{SQL CARBON EDIT}} - Remove test
|
||||
// {{SQL CARBON EDIT}} - Remove tests
|
||||
assert.equal(0, 0);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user