mirror of
https://github.com/microsoft/vscode.git
synced 2026-05-08 09:08:48 +01:00
Fix #33323
This commit is contained in:
@@ -598,4 +598,12 @@ export class SimpleWorkspaceContextService implements IWorkspaceContextService {
|
|||||||
public isCurrentWorkspace(workspaceIdentifier: ISingleFolderWorkspaceIdentifier | IWorkspaceIdentifier): boolean {
|
public isCurrentWorkspace(workspaceIdentifier: ISingleFolderWorkspaceIdentifier | IWorkspaceIdentifier): boolean {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public addFolders(foldersToAdd: URI[]): TPromise<void> {
|
||||||
|
return TPromise.as(void 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public removeFolders(foldersToRemove: URI[]): TPromise<void> {
|
||||||
|
return TPromise.as(void 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import Event from 'vs/base/common/event';
|
|||||||
import { ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier, IStoredWorkspaceFolder, isRawFileWorkspaceFolder, isRawUriWorkspaceFolder } from 'vs/platform/workspaces/common/workspaces';
|
import { ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier, IStoredWorkspaceFolder, isRawFileWorkspaceFolder, isRawUriWorkspaceFolder } from 'vs/platform/workspaces/common/workspaces';
|
||||||
import { coalesce, distinct } from 'vs/base/common/arrays';
|
import { coalesce, distinct } from 'vs/base/common/arrays';
|
||||||
import { isLinux } from 'vs/base/common/platform';
|
import { isLinux } from 'vs/base/common/platform';
|
||||||
|
import { TPromise } from 'vs/base/common/winjs.base';
|
||||||
|
|
||||||
export const IWorkspaceContextService = createDecorator<IWorkspaceContextService>('contextService');
|
export const IWorkspaceContextService = createDecorator<IWorkspaceContextService>('contextService');
|
||||||
|
|
||||||
@@ -31,21 +32,6 @@ export interface IWorkspaceFoldersChangeEvent {
|
|||||||
export interface IWorkspaceContextService {
|
export interface IWorkspaceContextService {
|
||||||
_serviceBrand: any;
|
_serviceBrand: any;
|
||||||
|
|
||||||
/**
|
|
||||||
* Provides access to the workspace object the platform is running with. This may be null if the workbench was opened
|
|
||||||
* without workspace (empty);
|
|
||||||
*/
|
|
||||||
getWorkspace(): IWorkspace;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the state of the workbench.
|
|
||||||
*
|
|
||||||
* WorkbenchState.EMPTY - if the workbench was opened with empty window or file
|
|
||||||
* WorkbenchState.FOLDER - if the workbench was opened with a folder
|
|
||||||
* WorkbenchState.WORKSPACE - if the workbench was opened with a workspace
|
|
||||||
*/
|
|
||||||
getWorkbenchState(): WorkbenchState;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An event which fires on workbench state changes.
|
* An event which fires on workbench state changes.
|
||||||
*/
|
*/
|
||||||
@@ -61,6 +47,31 @@ export interface IWorkspaceContextService {
|
|||||||
*/
|
*/
|
||||||
onDidChangeWorkspaceFolders: Event<IWorkspaceFoldersChangeEvent>;
|
onDidChangeWorkspaceFolders: Event<IWorkspaceFoldersChangeEvent>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides access to the workspace object the platform is running with. This may be null if the workbench was opened
|
||||||
|
* without workspace (empty);
|
||||||
|
*/
|
||||||
|
getWorkspace(): IWorkspace;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* add folders to the existing workspace
|
||||||
|
*/
|
||||||
|
addFolders(folders: URI[]): TPromise<void>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* remove folders from the existing workspace
|
||||||
|
*/
|
||||||
|
removeFolders(folders: URI[]): TPromise<void>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the state of the workbench.
|
||||||
|
*
|
||||||
|
* WorkbenchState.EMPTY - if the workbench was opened with empty window or file
|
||||||
|
* WorkbenchState.FOLDER - if the workbench was opened with a folder
|
||||||
|
* WorkbenchState.WORKSPACE - if the workbench was opened with a workspace
|
||||||
|
*/
|
||||||
|
getWorkbenchState(): WorkbenchState;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the folder for the given resource from the workspace.
|
* Returns the folder for the given resource from the workspace.
|
||||||
* Can be null if there is no workspace or the resource is not inside the workspace.
|
* Can be null if there is no workspace or the resource is not inside the workspace.
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
|||||||
import Event, { Emitter } from 'vs/base/common/event';
|
import Event, { Emitter } from 'vs/base/common/event';
|
||||||
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
|
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
|
||||||
import { IProgress } from 'vs/platform/progress/common/progress';
|
import { IProgress } from 'vs/platform/progress/common/progress';
|
||||||
import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing';
|
|
||||||
import { ISearchResultProvider, ISearchQuery, ISearchComplete, ISearchProgressItem, QueryType, IFileMatch, ISearchService } from 'vs/platform/search/common/search';
|
import { ISearchResultProvider, ISearchQuery, ISearchComplete, ISearchProgressItem, QueryType, IFileMatch, ISearchService } from 'vs/platform/search/common/search';
|
||||||
|
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||||
|
|
||||||
@extHostNamedCustomer(MainContext.MainThreadFileSystem)
|
@extHostNamedCustomer(MainContext.MainThreadFileSystem)
|
||||||
export class MainThreadFileSystem implements MainThreadFileSystemShape {
|
export class MainThreadFileSystem implements MainThreadFileSystemShape {
|
||||||
@@ -26,7 +26,7 @@ export class MainThreadFileSystem implements MainThreadFileSystemShape {
|
|||||||
extHostContext: IExtHostContext,
|
extHostContext: IExtHostContext,
|
||||||
@IFileService private readonly _fileService: IFileService,
|
@IFileService private readonly _fileService: IFileService,
|
||||||
@ISearchService private readonly _searchService: ISearchService,
|
@ISearchService private readonly _searchService: ISearchService,
|
||||||
@IWorkspaceEditingService private readonly _workspaceEditService: IWorkspaceEditingService
|
@IWorkspaceContextService private readonly _workspaceContextService: IWorkspaceContextService
|
||||||
) {
|
) {
|
||||||
this._proxy = extHostContext.get(ExtHostContext.ExtHostFileSystem);
|
this._proxy = extHostContext.get(ExtHostContext.ExtHostFileSystem);
|
||||||
}
|
}
|
||||||
@@ -45,7 +45,7 @@ export class MainThreadFileSystem implements MainThreadFileSystemShape {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$onDidAddFileSystemRoot(uri: URI): void {
|
$onDidAddFileSystemRoot(uri: URI): void {
|
||||||
this._workspaceEditService.addFolders([uri]);
|
this._workspaceContextService.addFolders([uri]);
|
||||||
}
|
}
|
||||||
|
|
||||||
$onFileSystemChange(handle: number, changes: IFileChange[]): void {
|
$onFileSystemChange(handle: number, changes: IFileChange[]): void {
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ export class AddRootFolderAction extends BaseWorkspacesAction {
|
|||||||
return TPromise.as(null);
|
return TPromise.as(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
addFoldersPromise = this.workspaceEditingService.addFolders(folders.map(folder => URI.file(folder)));
|
addFoldersPromise = this.contextService.addFolders(folders.map(folder => URI.file(folder)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Empty or Folder
|
// Empty or Folder
|
||||||
@@ -163,7 +163,7 @@ export class GlobalRemoveRootFolderAction extends BaseWorkspacesAction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Workspace: remove folder
|
// Workspace: remove folder
|
||||||
return this.workspaceEditingService.removeFolders([folder.uri]).then(() => true);
|
return this.contextService.removeFolders([folder.uri]).then(() => true);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -216,13 +216,13 @@ export class RemoveRootFolderAction extends Action {
|
|||||||
private rootUri: URI,
|
private rootUri: URI,
|
||||||
id: string,
|
id: string,
|
||||||
label: string,
|
label: string,
|
||||||
@IWorkspaceEditingService private workspaceEditingService: IWorkspaceEditingService
|
@IWorkspaceContextService private contextService: IWorkspaceContextService
|
||||||
) {
|
) {
|
||||||
super(id, label);
|
super(id, label);
|
||||||
}
|
}
|
||||||
|
|
||||||
public run(): TPromise<any> {
|
public run(): TPromise<any> {
|
||||||
return this.workspaceEditingService.removeFolders([this.rootUri]);
|
return this.contextService.removeFolders([this.rootUri]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -400,7 +400,7 @@ export class ElectronWindow extends Themable {
|
|||||||
|
|
||||||
// Workspace: just add to workspace config
|
// Workspace: just add to workspace config
|
||||||
if (this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE) {
|
if (this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE) {
|
||||||
this.workspaceEditingService.addFolders(foldersToAdd).done(null, errors.onUnexpectedError);
|
this.contextService.addFolders(foldersToAdd).done(null, errors.onUnexpectedError);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Single folder or no workspace: create workspace and open
|
// Single folder or no workspace: create workspace and open
|
||||||
|
|||||||
@@ -910,7 +910,7 @@ export class FileDragAndDrop extends SimpleFileResourceDragAndDrop {
|
|||||||
const folders = result.filter(result => result.stat.isDirectory).map(result => result.stat.resource);
|
const folders = result.filter(result => result.stat.isDirectory).map(result => result.stat.resource);
|
||||||
if (folders.length > 0) {
|
if (folders.length > 0) {
|
||||||
if (this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE) {
|
if (this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE) {
|
||||||
return this.workspaceEditingService.addFolders(folders);
|
return this.contextService.addFolders(folders);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we are in single-folder context, ask for confirmation to create a workspace
|
// If we are in single-folder context, ask for confirmation to create a workspace
|
||||||
|
|||||||
@@ -0,0 +1,286 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
import URI from 'vs/base/common/uri';
|
||||||
|
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 * 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 { isLinux } from 'vs/base/common/platform';
|
||||||
|
import { ConfigWatcher } from 'vs/base/node/config';
|
||||||
|
import { CustomConfigurationModel, ConfigurationModel } from 'vs/platform/configuration/common/configurationModels';
|
||||||
|
import { WorkspaceConfigurationModel, ScopedConfigurationModel, FolderConfigurationModel, FolderSettingsModel } from 'vs/workbench/services/configuration/common/configurationModels';
|
||||||
|
import { WORKSPACE_STANDALONE_CONFIGURATIONS, WORKSPACE_CONFIG_DEFAULT_PATH, TASKS_CONFIGURATION_KEY, LAUNCH_CONFIGURATION_KEY } from 'vs/workbench/services/configuration/common/configuration';
|
||||||
|
import { ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry';
|
||||||
|
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';
|
||||||
|
|
||||||
|
// 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)) }; })
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export class WorkspaceConfiguration extends Disposable {
|
||||||
|
|
||||||
|
private _workspaceConfigPath: URI;
|
||||||
|
private _workspaceConfigurationWatcher: ConfigWatcher<WorkspaceConfigurationModel>;
|
||||||
|
private _workspaceConfigurationWatcherDisposables: IDisposable[] = [];
|
||||||
|
|
||||||
|
private _onDidUpdateConfiguration: Emitter<void> = this._register(new Emitter<void>());
|
||||||
|
public readonly onDidUpdateConfiguration: Event<void> = this._onDidUpdateConfiguration.event;
|
||||||
|
|
||||||
|
load(workspaceConfigPath: URI): TPromise<void> {
|
||||||
|
if (this._workspaceConfigPath && this._workspaceConfigPath.fsPath === workspaceConfigPath.fsPath) {
|
||||||
|
return this.reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
this._workspaceConfigPath = workspaceConfigPath;
|
||||||
|
|
||||||
|
this._workspaceConfigurationWatcherDisposables = dispose(this._workspaceConfigurationWatcherDisposables);
|
||||||
|
return new TPromise<void>((c, e) => {
|
||||||
|
this._workspaceConfigurationWatcher = new ConfigWatcher(this._workspaceConfigPath.fsPath, {
|
||||||
|
changeBufferDelay: 300,
|
||||||
|
onError: error => errors.onUnexpectedError(error),
|
||||||
|
defaultConfig: new WorkspaceConfigurationModel(JSON.stringify({ folders: [] } as IStoredWorkspace, null, '\t'), this._workspaceConfigPath.fsPath),
|
||||||
|
parse: (content: string, parseErrors: any[]) => {
|
||||||
|
const workspaceConfigurationModel = new WorkspaceConfigurationModel(content, this._workspaceConfigPath.fsPath);
|
||||||
|
parseErrors = [...workspaceConfigurationModel.errors];
|
||||||
|
return workspaceConfigurationModel;
|
||||||
|
}, initCallback: () => c(null)
|
||||||
|
});
|
||||||
|
this._workspaceConfigurationWatcherDisposables.push(this._workspaceConfigurationWatcher);
|
||||||
|
this._workspaceConfigurationWatcher.onDidUpdateConfiguration(() => this._onDidUpdateConfiguration.fire(), this, this._workspaceConfigurationWatcherDisposables);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private get workspaceConfigurationModel(): WorkspaceConfigurationModel {
|
||||||
|
return this._workspaceConfigurationWatcher ? this._workspaceConfigurationWatcher.getConfig() : new WorkspaceConfigurationModel();
|
||||||
|
}
|
||||||
|
|
||||||
|
reload(): TPromise<void> {
|
||||||
|
return new TPromise<void>(c => this._workspaceConfigurationWatcher.reload(() => c(null)));
|
||||||
|
}
|
||||||
|
|
||||||
|
getFolders(): IStoredWorkspaceFolder[] {
|
||||||
|
return this.workspaceConfigurationModel.folders;
|
||||||
|
}
|
||||||
|
|
||||||
|
setFolders(folders: IStoredWorkspaceFolder[], jsonEditingService: JSONEditingService): TPromise<void> {
|
||||||
|
return jsonEditingService.write(this._workspaceConfigPath, { key: 'folders', value: folders }, true)
|
||||||
|
.then(() => this.reload());
|
||||||
|
}
|
||||||
|
|
||||||
|
getConfiguration(): ConfigurationModel {
|
||||||
|
return this.workspaceConfigurationModel.workspaceConfiguration;
|
||||||
|
}
|
||||||
|
|
||||||
|
dispose(): void {
|
||||||
|
dispose(this._workspaceConfigurationWatcherDisposables);
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class FolderConfiguration extends Disposable {
|
||||||
|
|
||||||
|
private static RELOAD_CONFIGURATION_DELAY = 50;
|
||||||
|
|
||||||
|
private bulkFetchFromWorkspacePromise: TPromise;
|
||||||
|
private workspaceFilePathToConfiguration: { [relativeWorkspacePath: string]: TPromise<ConfigurationModel> };
|
||||||
|
|
||||||
|
private reloadConfigurationScheduler: RunOnceScheduler;
|
||||||
|
private reloadConfigurationEventEmitter: Emitter<FolderConfigurationModel> = new Emitter<FolderConfigurationModel>();
|
||||||
|
|
||||||
|
constructor(private folder: URI, private configFolderRelativePath: string, private scope: ConfigurationScope) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
loadConfiguration(): TPromise<FolderConfigurationModel> {
|
||||||
|
// Load workspace locals
|
||||||
|
return this.loadWorkspaceConfigFiles().then(workspaceConfigFiles => {
|
||||||
|
// Consolidate (support *.json files in the workspace settings folder)
|
||||||
|
const workspaceSettingsConfig = <FolderSettingsModel>workspaceConfigFiles[WORKSPACE_CONFIG_DEFAULT_PATH] || new FolderSettingsModel(null);
|
||||||
|
const otherConfigModels = Object.keys(workspaceConfigFiles).filter(key => key !== WORKSPACE_CONFIG_DEFAULT_PATH).map(key => <ScopedConfigurationModel>workspaceConfigFiles[key]);
|
||||||
|
return new FolderConfigurationModel(workspaceSettingsConfig, otherConfigModels, this.scope);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private loadWorkspaceConfigFiles(): TPromise<{ [relativeWorkspacePath: string]: ConfigurationModel }> {
|
||||||
|
// 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([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return resolveContents(stat.children.filter(stat => {
|
||||||
|
const isJson = paths.extname(stat.resource.fsPath) === '.json';
|
||||||
|
if (!isJson) {
|
||||||
|
return false; // only JSON files
|
||||||
|
}
|
||||||
|
|
||||||
|
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.createConfigModel(content)));
|
||||||
|
}, errors.onUnexpectedError);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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));
|
||||||
|
}
|
||||||
|
|
||||||
|
public handleWorkspaceFileEvents(event: FileChangesEvent): TPromise<FolderConfigurationModel> {
|
||||||
|
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));
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle case where ".vscode" got deleted
|
||||||
|
if (workspacePath === this.configFolderRelativePath && events[i].type === FileChangeType.DELETED) {
|
||||||
|
this.workspaceFilePathToConfiguration = Object.create(null);
|
||||||
|
affectedByChanges = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// only valid workspace config files
|
||||||
|
if (!this.isWorkspaceConfigurationFile(workspacePath)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// insert 'fetch-promises' for add and update events and
|
||||||
|
// remove promises for delete events
|
||||||
|
switch (events[i].type) {
|
||||||
|
case FileChangeType.DELETED:
|
||||||
|
affectedByChanges = collections.remove(this.workspaceFilePathToConfiguration, workspacePath);
|
||||||
|
break;
|
||||||
|
case FileChangeType.UPDATED:
|
||||||
|
case FileChangeType.ADDED:
|
||||||
|
this.workspaceFilePathToConfiguration[workspacePath] = resolveContent(resource).then(content => this.createConfigModel(content), errors.onUnexpectedError);
|
||||||
|
affectedByChanges = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!affectedByChanges) {
|
||||||
|
return TPromise.as(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 createConfigModel(content: IContent): ConfigurationModel {
|
||||||
|
const path = this.toFolderRelativePath(content.resource);
|
||||||
|
if (path === WORKSPACE_CONFIG_DEFAULT_PATH) {
|
||||||
|
return new FolderSettingsModel(content.value, content.resource.toString());
|
||||||
|
} else {
|
||||||
|
const matches = /\/([^\.]*)*\.json/.exec(path);
|
||||||
|
if (matches && matches[1]) {
|
||||||
|
return new ScopedConfigurationModel(content.value, content.resource.toString(), matches[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new CustomConfigurationModel(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private isWorkspaceConfigurationFile(folderRelativePath: string): boolean {
|
||||||
|
return [WORKSPACE_CONFIG_DEFAULT_PATH, WORKSPACE_STANDALONE_CONFIGURATIONS[TASKS_CONFIGURATION_KEY], WORKSPACE_STANDALONE_CONFIGURATIONS[LAUNCH_CONFIGURATION_KEY]].some(p => p === folderRelativePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
private toResource(folderRelativePath: string): URI {
|
||||||
|
if (typeof folderRelativePath === 'string') {
|
||||||
|
return URI.file(paths.join(this.folder.fsPath, folderRelativePath));
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private toFolderRelativePath(resource: URI, toOSPath?: boolean): string {
|
||||||
|
if (this.contains(resource)) {
|
||||||
|
return paths.normalize(paths.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,31 +7,28 @@
|
|||||||
import URI from 'vs/base/common/uri';
|
import URI from 'vs/base/common/uri';
|
||||||
import * as paths from 'vs/base/common/paths';
|
import * as paths from 'vs/base/common/paths';
|
||||||
import { TPromise } from 'vs/base/common/winjs.base';
|
import { TPromise } from 'vs/base/common/winjs.base';
|
||||||
|
import { dirname } from 'path';
|
||||||
import * as assert from 'vs/base/common/assert';
|
import * as assert from 'vs/base/common/assert';
|
||||||
import Event, { Emitter } from 'vs/base/common/event';
|
import Event, { Emitter } from 'vs/base/common/event';
|
||||||
import { StrictResourceMap } from 'vs/base/common/map';
|
import { StrictResourceMap } from 'vs/base/common/map';
|
||||||
import * as errors from 'vs/base/common/errors';
|
|
||||||
import { equals } from 'vs/base/common/objects';
|
import { equals } from 'vs/base/common/objects';
|
||||||
import * as collections from 'vs/base/common/collections';
|
import { Disposable } from 'vs/base/common/lifecycle';
|
||||||
import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle';
|
import { Queue } from 'vs/base/common/async';
|
||||||
import { RunOnceScheduler } from 'vs/base/common/async';
|
import { stat, writeFile } from 'vs/base/node/pfs';
|
||||||
import { readFile, stat, writeFile } from 'vs/base/node/pfs';
|
|
||||||
import { IJSONContributionRegistry, Extensions as JSONExtensions } from 'vs/platform/jsonschemas/common/jsonContributionRegistry';
|
import { IJSONContributionRegistry, Extensions as JSONExtensions } from 'vs/platform/jsonschemas/common/jsonContributionRegistry';
|
||||||
import * as extfs from 'vs/base/node/extfs';
|
|
||||||
import { IWorkspaceContextService, Workspace, WorkbenchState, IWorkspaceFolder, toWorkspaceFolders, IWorkspaceFoldersChangeEvent } from 'vs/platform/workspace/common/workspace';
|
import { IWorkspaceContextService, Workspace, WorkbenchState, IWorkspaceFolder, toWorkspaceFolders, IWorkspaceFoldersChangeEvent } from 'vs/platform/workspace/common/workspace';
|
||||||
import { FileChangeType, FileChangesEvent } from 'vs/platform/files/common/files';
|
import { FileChangesEvent } from 'vs/platform/files/common/files';
|
||||||
import { isLinux } from 'vs/base/common/platform';
|
import { isLinux } from 'vs/base/common/platform';
|
||||||
import { ConfigWatcher } from 'vs/base/node/config';
|
|
||||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||||
import { CustomConfigurationModel, ConfigurationModel, ConfigurationChangeEvent, AllKeysConfigurationChangeEvent } from 'vs/platform/configuration/common/configurationModels';
|
import { ConfigurationModel, ConfigurationChangeEvent, AllKeysConfigurationChangeEvent } from 'vs/platform/configuration/common/configurationModels';
|
||||||
import { IConfigurationChangeEvent, ConfigurationTarget, IConfigurationOverrides, keyFromOverrideIdentifier, isConfigurationOverrides, IConfigurationData } from 'vs/platform/configuration/common/configuration';
|
import { IConfigurationChangeEvent, ConfigurationTarget, IConfigurationOverrides, keyFromOverrideIdentifier, isConfigurationOverrides, IConfigurationData } from 'vs/platform/configuration/common/configuration';
|
||||||
import { WorkspaceConfigurationModel, ScopedConfigurationModel, FolderConfigurationModel, FolderSettingsModel, Configuration, WorkspaceConfigurationChangeEvent } from 'vs/workbench/services/configuration/common/configurationModels';
|
import { FolderConfigurationModel, Configuration, WorkspaceConfigurationChangeEvent } from 'vs/workbench/services/configuration/common/configurationModels';
|
||||||
import { IWorkspaceConfigurationService, WORKSPACE_CONFIG_FOLDER_DEFAULT_NAME, WORKSPACE_STANDALONE_CONFIGURATIONS, WORKSPACE_CONFIG_DEFAULT_PATH, TASKS_CONFIGURATION_KEY, LAUNCH_CONFIGURATION_KEY, defaultSettingsSchemaId, userSettingsSchemaId, workspaceSettingsSchemaId, folderSettingsSchemaId } from 'vs/workbench/services/configuration/common/configuration';
|
import { IWorkspaceConfigurationService, WORKSPACE_CONFIG_FOLDER_DEFAULT_NAME, defaultSettingsSchemaId, userSettingsSchemaId, workspaceSettingsSchemaId, folderSettingsSchemaId } from 'vs/workbench/services/configuration/common/configuration';
|
||||||
import { ConfigurationService as GlobalConfigurationService } from 'vs/platform/configuration/node/configurationService';
|
import { ConfigurationService as GlobalConfigurationService } from 'vs/platform/configuration/node/configurationService';
|
||||||
import { Registry } from 'vs/platform/registry/common/platform';
|
import { Registry } from 'vs/platform/registry/common/platform';
|
||||||
import { IConfigurationNode, IConfigurationRegistry, Extensions, ConfigurationScope, settingsSchema, resourceSettingsSchema } from 'vs/platform/configuration/common/configurationRegistry';
|
import { IConfigurationNode, IConfigurationRegistry, Extensions, ConfigurationScope, settingsSchema, resourceSettingsSchema } from 'vs/platform/configuration/common/configurationRegistry';
|
||||||
import { createHash } from 'crypto';
|
import { createHash } from 'crypto';
|
||||||
import { getWorkspaceLabel, IWorkspacesService, IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier, IStoredWorkspace } from 'vs/platform/workspaces/common/workspaces';
|
import { getWorkspaceLabel, IWorkspacesService, IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier, IStoredWorkspaceFolder, isStoredWorkspaceFolder } from 'vs/platform/workspaces/common/workspaces';
|
||||||
import { IWindowConfiguration } from 'vs/platform/windows/common/windows';
|
import { IWindowConfiguration } from 'vs/platform/windows/common/windows';
|
||||||
import { IExtensionService } from 'vs/platform/extensions/common/extensions';
|
import { IExtensionService } from 'vs/platform/extensions/common/extensions';
|
||||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||||
@@ -39,17 +36,10 @@ import product from 'vs/platform/node/product';
|
|||||||
import pkg from 'vs/platform/node/package';
|
import pkg from 'vs/platform/node/package';
|
||||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||||
import { ConfigurationEditingService } from 'vs/workbench/services/configuration/node/configurationEditingService';
|
import { ConfigurationEditingService } from 'vs/workbench/services/configuration/node/configurationEditingService';
|
||||||
|
import { WorkspaceConfiguration, FolderConfiguration } from 'vs/workbench/services/configuration/node/configuration';
|
||||||
interface IStat {
|
import { JSONEditingService } from 'vs/workbench/services/configuration/node/jsonEditingService';
|
||||||
resource: URI;
|
import { Schemas } from 'vs/base/common/network';
|
||||||
isDirectory?: boolean;
|
import { massageFolderPathForWorkspace } from 'vs/platform/workspaces/node/workspaces';
|
||||||
children?: { resource: URI; }[];
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IContent {
|
|
||||||
resource: URI;
|
|
||||||
value: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class WorkspaceService extends Disposable implements IWorkspaceConfigurationService, IWorkspaceContextService {
|
export class WorkspaceService extends Disposable implements IWorkspaceConfigurationService, IWorkspaceContextService {
|
||||||
|
|
||||||
@@ -61,6 +51,8 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat
|
|||||||
private workspaceConfiguration: WorkspaceConfiguration;
|
private workspaceConfiguration: WorkspaceConfiguration;
|
||||||
private cachedFolderConfigs: StrictResourceMap<FolderConfiguration>;
|
private cachedFolderConfigs: StrictResourceMap<FolderConfiguration>;
|
||||||
|
|
||||||
|
private workspaceEditingQueue: Queue<void>;
|
||||||
|
|
||||||
protected readonly _onDidChangeConfiguration: Emitter<IConfigurationChangeEvent> = this._register(new Emitter<IConfigurationChangeEvent>());
|
protected readonly _onDidChangeConfiguration: Emitter<IConfigurationChangeEvent> = this._register(new Emitter<IConfigurationChangeEvent>());
|
||||||
public readonly onDidChangeConfiguration: Event<IConfigurationChangeEvent> = this._onDidChangeConfiguration.event;
|
public readonly onDidChangeConfiguration: Event<IConfigurationChangeEvent> = this._onDidChangeConfiguration.event;
|
||||||
|
|
||||||
@@ -74,6 +66,7 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat
|
|||||||
public readonly onDidChangeWorkbenchState: Event<WorkbenchState> = this._onDidChangeWorkbenchState.event;
|
public readonly onDidChangeWorkbenchState: Event<WorkbenchState> = this._onDidChangeWorkbenchState.event;
|
||||||
|
|
||||||
private configurationEditingService: ConfigurationEditingService;
|
private configurationEditingService: ConfigurationEditingService;
|
||||||
|
private jsonEditingService: JSONEditingService;
|
||||||
|
|
||||||
constructor(private environmentService: IEnvironmentService, private workspacesService: IWorkspacesService, private workspaceSettingsRootFolder: string = WORKSPACE_CONFIG_FOLDER_DEFAULT_NAME) {
|
constructor(private environmentService: IEnvironmentService, private workspacesService: IWorkspacesService, private workspaceSettingsRootFolder: string = WORKSPACE_CONFIG_FOLDER_DEFAULT_NAME) {
|
||||||
super();
|
super();
|
||||||
@@ -84,6 +77,8 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat
|
|||||||
this.baseConfigurationService = this._register(new GlobalConfigurationService(environmentService));
|
this.baseConfigurationService = this._register(new GlobalConfigurationService(environmentService));
|
||||||
this._register(this.baseConfigurationService.onDidChangeConfiguration(e => this.onBaseConfigurationChanged(e)));
|
this._register(this.baseConfigurationService.onDidChangeConfiguration(e => this.onBaseConfigurationChanged(e)));
|
||||||
this._register(Registry.as<IConfigurationRegistry>(Extensions.Configuration).onDidRegisterConfiguration(e => this.registerConfigurationSchemas()));
|
this._register(Registry.as<IConfigurationRegistry>(Extensions.Configuration).onDidRegisterConfiguration(e => this.registerConfigurationSchemas()));
|
||||||
|
|
||||||
|
this.workspaceEditingQueue = new Queue<void>();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Workspace Context Service Impl
|
// Workspace Context Service Impl
|
||||||
@@ -111,6 +106,16 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat
|
|||||||
return this.workspace.getFolder(resource);
|
return this.workspace.getFolder(resource);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public addFolders(foldersToAdd: URI[]): TPromise<void> {
|
||||||
|
assert.ok(this.jsonEditingService, 'Workbench is not initialized yet');
|
||||||
|
return this.workspaceEditingQueue.queue(() => this.doAddFolders(foldersToAdd));
|
||||||
|
}
|
||||||
|
|
||||||
|
public removeFolders(foldersToRemove: URI[]): TPromise<void> {
|
||||||
|
assert.ok(this.jsonEditingService, 'Workbench is not initialized yet');
|
||||||
|
return this.workspaceEditingQueue.queue(() => this.doRemoveFolders(foldersToRemove));
|
||||||
|
}
|
||||||
|
|
||||||
public isInsideWorkspace(resource: URI): boolean {
|
public isInsideWorkspace(resource: URI): boolean {
|
||||||
return !!this.getWorkspaceFolder(resource);
|
return !!this.getWorkspaceFolder(resource);
|
||||||
}
|
}
|
||||||
@@ -125,6 +130,79 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private doAddFolders(foldersToAdd: URI[]): TPromise<void> {
|
||||||
|
if (this.getWorkbenchState() !== WorkbenchState.WORKSPACE) {
|
||||||
|
return TPromise.as(void 0); // we need a workspace to begin with
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentWorkspaceFolders = this.getWorkspace().folders;
|
||||||
|
const currentWorkspaceFolderUris = currentWorkspaceFolders.map(folder => folder.uri);
|
||||||
|
const currentStoredFolders = currentWorkspaceFolders.map(folder => folder.raw);
|
||||||
|
|
||||||
|
const storedFoldersToAdd: IStoredWorkspaceFolder[] = [];
|
||||||
|
|
||||||
|
const workspaceConfigFolder = dirname(this.getWorkspace().configuration.fsPath);
|
||||||
|
|
||||||
|
foldersToAdd.forEach(folderToAdd => {
|
||||||
|
if (this.contains(currentWorkspaceFolderUris, folderToAdd)) {
|
||||||
|
return; // already existing
|
||||||
|
}
|
||||||
|
|
||||||
|
// File resource: use "path" property
|
||||||
|
if (folderToAdd.scheme === Schemas.file) {
|
||||||
|
storedFoldersToAdd.push({
|
||||||
|
path: massageFolderPathForWorkspace(folderToAdd.fsPath, workspaceConfigFolder, currentStoredFolders)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Any other resource: use "uri" property
|
||||||
|
else {
|
||||||
|
storedFoldersToAdd.push({
|
||||||
|
uri: folderToAdd.toString(true)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (storedFoldersToAdd.length > 0) {
|
||||||
|
return this.workspaceConfiguration.setFolders([...currentStoredFolders, ...storedFoldersToAdd], this.jsonEditingService);
|
||||||
|
}
|
||||||
|
|
||||||
|
return TPromise.as(void 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private doRemoveFolders(foldersToRemove: URI[]): TPromise<void> {
|
||||||
|
if (this.getWorkbenchState() !== WorkbenchState.WORKSPACE) {
|
||||||
|
return TPromise.as(void 0); // we need a workspace to begin with
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentWorkspaceFolders = this.getWorkspace().folders;
|
||||||
|
const currentStoredFolders = currentWorkspaceFolders.map(folder => folder.raw);
|
||||||
|
|
||||||
|
const newStoredFolders: IStoredWorkspaceFolder[] = currentStoredFolders.filter((folder, index) => {
|
||||||
|
if (!isStoredWorkspaceFolder(folder)) {
|
||||||
|
return true; // keep entries which are unrelated
|
||||||
|
}
|
||||||
|
|
||||||
|
return !this.contains(foldersToRemove, currentWorkspaceFolders[index].uri); // keep entries which are unrelated
|
||||||
|
});
|
||||||
|
|
||||||
|
if (newStoredFolders.length !== currentStoredFolders.length) {
|
||||||
|
return this.workspaceConfiguration.setFolders(newStoredFolders, this.jsonEditingService);
|
||||||
|
}
|
||||||
|
|
||||||
|
return TPromise.as(void 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private contains(resources: URI[], toCheck: URI): boolean {
|
||||||
|
return resources.some(resource => {
|
||||||
|
if (isLinux) {
|
||||||
|
return resource.toString() === toCheck.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
return resource.toString().toLowerCase() === toCheck.toString().toLowerCase();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Workspace Configuration Service Impl
|
// Workspace Configuration Service Impl
|
||||||
|
|
||||||
getConfigurationData(): IConfigurationData {
|
getConfigurationData(): IConfigurationData {
|
||||||
@@ -198,6 +276,7 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat
|
|||||||
|
|
||||||
setInstantiationService(instantiationService: IInstantiationService): void {
|
setInstantiationService(instantiationService: IInstantiationService): void {
|
||||||
this.configurationEditingService = instantiationService.createInstance(ConfigurationEditingService);
|
this.configurationEditingService = instantiationService.createInstance(ConfigurationEditingService);
|
||||||
|
this.jsonEditingService = instantiationService.createInstance(JSONEditingService);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleWorkspaceFileEvents(event: FileChangesEvent): TPromise<void> {
|
handleWorkspaceFileEvents(event: FileChangesEvent): TPromise<void> {
|
||||||
@@ -226,8 +305,7 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat
|
|||||||
const workspaceConfigPath = URI.file(workspaceIdentifier.configPath);
|
const workspaceConfigPath = URI.file(workspaceIdentifier.configPath);
|
||||||
return this.workspaceConfiguration.load(workspaceConfigPath)
|
return this.workspaceConfiguration.load(workspaceConfigPath)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
const workspaceConfigurationModel = this.workspaceConfiguration.workspaceConfigurationModel;
|
const workspaceFolders = toWorkspaceFolders(this.workspaceConfiguration.getFolders(), URI.file(paths.dirname(workspaceConfigPath.fsPath)));
|
||||||
const workspaceFolders = toWorkspaceFolders(workspaceConfigurationModel.folders, URI.file(paths.dirname(workspaceConfigPath.fsPath)));
|
|
||||||
const workspaceId = workspaceIdentifier.id;
|
const workspaceId = workspaceIdentifier.id;
|
||||||
const workspaceName = getWorkspaceLabel({ id: workspaceId, configPath: workspaceConfigPath.fsPath }, this.environmentService);
|
const workspaceName = getWorkspaceLabel({ id: workspaceId, configPath: workspaceConfigPath.fsPath }, this.environmentService);
|
||||||
return new Workspace(workspaceId, workspaceName, workspaceFolders, workspaceConfigPath);
|
return new Workspace(workspaceId, workspaceName, workspaceFolders, workspaceConfigPath);
|
||||||
@@ -341,7 +419,7 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat
|
|||||||
case WorkbenchState.FOLDER:
|
case WorkbenchState.FOLDER:
|
||||||
return folderConfigurations[0];
|
return folderConfigurations[0];
|
||||||
case WorkbenchState.WORKSPACE:
|
case WorkbenchState.WORKSPACE:
|
||||||
return this.workspaceConfiguration.workspaceConfigurationModel.workspaceConfiguration;
|
return this.workspaceConfiguration.getConfiguration();
|
||||||
default:
|
default:
|
||||||
return new ConfigurationModel();
|
return new ConfigurationModel();
|
||||||
}
|
}
|
||||||
@@ -378,8 +456,8 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat
|
|||||||
|
|
||||||
private onWorkspaceConfigurationChanged(): TPromise<void> {
|
private onWorkspaceConfigurationChanged(): TPromise<void> {
|
||||||
if (this.workspace && this.workspace.configuration && this._configuration) {
|
if (this.workspace && this.workspace.configuration && this._configuration) {
|
||||||
const workspaceConfigurationChangeEvent = this._configuration.updateWorkspaceConfiguration(this.workspaceConfiguration.workspaceConfigurationModel.workspaceConfiguration);
|
const workspaceConfigurationChangeEvent = this._configuration.updateWorkspaceConfiguration(this.workspaceConfiguration.getConfiguration());
|
||||||
let configuredFolders = toWorkspaceFolders(this.workspaceConfiguration.workspaceConfigurationModel.folders, URI.file(paths.dirname(this.workspace.configuration.fsPath)));
|
let configuredFolders = toWorkspaceFolders(this.workspaceConfiguration.getFolders(), URI.file(paths.dirname(this.workspace.configuration.fsPath)));
|
||||||
const changes = this.compareFolders(this.workspace.folders, configuredFolders);
|
const changes = this.compareFolders(this.workspace.folders, configuredFolders);
|
||||||
if (changes.added.length || changes.removed.length || changes.changed.length) {
|
if (changes.added.length || changes.removed.length || changes.changed.length) {
|
||||||
this.workspace.folders = configuredFolders;
|
this.workspace.folders = configuredFolders;
|
||||||
@@ -557,244 +635,6 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class WorkspaceConfiguration extends Disposable {
|
|
||||||
|
|
||||||
private _workspaceConfigPath: URI;
|
|
||||||
private _workspaceConfigurationWatcher: ConfigWatcher<WorkspaceConfigurationModel>;
|
|
||||||
private _workspaceConfigurationWatcherDisposables: IDisposable[] = [];
|
|
||||||
|
|
||||||
private _onDidUpdateConfiguration: Emitter<void> = this._register(new Emitter<void>());
|
|
||||||
public readonly onDidUpdateConfiguration: Event<void> = this._onDidUpdateConfiguration.event;
|
|
||||||
|
|
||||||
load(workspaceConfigPath: URI): TPromise<void> {
|
|
||||||
if (this._workspaceConfigPath && this._workspaceConfigPath.fsPath === workspaceConfigPath.fsPath) {
|
|
||||||
return this.reload();
|
|
||||||
}
|
|
||||||
|
|
||||||
this._workspaceConfigPath = workspaceConfigPath;
|
|
||||||
|
|
||||||
this._workspaceConfigurationWatcherDisposables = dispose(this._workspaceConfigurationWatcherDisposables);
|
|
||||||
return new TPromise<void>((c, e) => {
|
|
||||||
this._workspaceConfigurationWatcher = new ConfigWatcher(this._workspaceConfigPath.fsPath, {
|
|
||||||
changeBufferDelay: 300,
|
|
||||||
onError: error => errors.onUnexpectedError(error),
|
|
||||||
defaultConfig: new WorkspaceConfigurationModel(JSON.stringify({ folders: [] } as IStoredWorkspace, null, '\t'), this._workspaceConfigPath.fsPath),
|
|
||||||
parse: (content: string, parseErrors: any[]) => {
|
|
||||||
const workspaceConfigurationModel = new WorkspaceConfigurationModel(content, this._workspaceConfigPath.fsPath);
|
|
||||||
parseErrors = [...workspaceConfigurationModel.errors];
|
|
||||||
return workspaceConfigurationModel;
|
|
||||||
}, initCallback: () => c(null)
|
|
||||||
});
|
|
||||||
this._workspaceConfigurationWatcherDisposables.push(this._workspaceConfigurationWatcher);
|
|
||||||
this._workspaceConfigurationWatcher.onDidUpdateConfiguration(() => this._onDidUpdateConfiguration.fire(), this, this._workspaceConfigurationWatcherDisposables);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
get workspaceConfigurationModel(): WorkspaceConfigurationModel {
|
|
||||||
return this._workspaceConfigurationWatcher ? this._workspaceConfigurationWatcher.getConfig() : new WorkspaceConfigurationModel();
|
|
||||||
}
|
|
||||||
|
|
||||||
reload(): TPromise<void> {
|
|
||||||
return new TPromise<void>(c => this._workspaceConfigurationWatcher.reload(() => c(null)));
|
|
||||||
}
|
|
||||||
|
|
||||||
dispose(): void {
|
|
||||||
dispose(this._workspaceConfigurationWatcherDisposables);
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class FolderConfiguration extends Disposable {
|
|
||||||
|
|
||||||
private static RELOAD_CONFIGURATION_DELAY = 50;
|
|
||||||
|
|
||||||
private bulkFetchFromWorkspacePromise: TPromise;
|
|
||||||
private workspaceFilePathToConfiguration: { [relativeWorkspacePath: string]: TPromise<ConfigurationModel> };
|
|
||||||
|
|
||||||
private reloadConfigurationScheduler: RunOnceScheduler;
|
|
||||||
private reloadConfigurationEventEmitter: Emitter<FolderConfigurationModel> = new Emitter<FolderConfigurationModel>();
|
|
||||||
|
|
||||||
constructor(private folder: URI, private configFolderRelativePath: string, private scope: ConfigurationScope) {
|
|
||||||
super();
|
|
||||||
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
|
|
||||||
loadConfiguration(): TPromise<FolderConfigurationModel> {
|
|
||||||
// Load workspace locals
|
|
||||||
return this.loadWorkspaceConfigFiles().then(workspaceConfigFiles => {
|
|
||||||
// Consolidate (support *.json files in the workspace settings folder)
|
|
||||||
const workspaceSettingsConfig = <FolderSettingsModel>workspaceConfigFiles[WORKSPACE_CONFIG_DEFAULT_PATH] || new FolderSettingsModel(null);
|
|
||||||
const otherConfigModels = Object.keys(workspaceConfigFiles).filter(key => key !== WORKSPACE_CONFIG_DEFAULT_PATH).map(key => <ScopedConfigurationModel>workspaceConfigFiles[key]);
|
|
||||||
return new FolderConfigurationModel(workspaceSettingsConfig, otherConfigModels, this.scope);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private loadWorkspaceConfigFiles(): TPromise<{ [relativeWorkspacePath: string]: ConfigurationModel }> {
|
|
||||||
// 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([]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return resolveContents(stat.children.filter(stat => {
|
|
||||||
const isJson = paths.extname(stat.resource.fsPath) === '.json';
|
|
||||||
if (!isJson) {
|
|
||||||
return false; // only JSON files
|
|
||||||
}
|
|
||||||
|
|
||||||
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.createConfigModel(content)));
|
|
||||||
}, errors.onUnexpectedError);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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));
|
|
||||||
}
|
|
||||||
|
|
||||||
public handleWorkspaceFileEvents(event: FileChangesEvent): TPromise<FolderConfigurationModel> {
|
|
||||||
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));
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle case where ".vscode" got deleted
|
|
||||||
if (workspacePath === this.configFolderRelativePath && events[i].type === FileChangeType.DELETED) {
|
|
||||||
this.workspaceFilePathToConfiguration = Object.create(null);
|
|
||||||
affectedByChanges = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// only valid workspace config files
|
|
||||||
if (!this.isWorkspaceConfigurationFile(workspacePath)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// insert 'fetch-promises' for add and update events and
|
|
||||||
// remove promises for delete events
|
|
||||||
switch (events[i].type) {
|
|
||||||
case FileChangeType.DELETED:
|
|
||||||
affectedByChanges = collections.remove(this.workspaceFilePathToConfiguration, workspacePath);
|
|
||||||
break;
|
|
||||||
case FileChangeType.UPDATED:
|
|
||||||
case FileChangeType.ADDED:
|
|
||||||
this.workspaceFilePathToConfiguration[workspacePath] = resolveContent(resource).then(content => this.createConfigModel(content), errors.onUnexpectedError);
|
|
||||||
affectedByChanges = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!affectedByChanges) {
|
|
||||||
return TPromise.as(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
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 createConfigModel(content: IContent): ConfigurationModel {
|
|
||||||
const path = this.toFolderRelativePath(content.resource);
|
|
||||||
if (path === WORKSPACE_CONFIG_DEFAULT_PATH) {
|
|
||||||
return new FolderSettingsModel(content.value, content.resource.toString());
|
|
||||||
} else {
|
|
||||||
const matches = /\/([^\.]*)*\.json/.exec(path);
|
|
||||||
if (matches && matches[1]) {
|
|
||||||
return new ScopedConfigurationModel(content.value, content.resource.toString(), matches[1]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return new CustomConfigurationModel(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private isWorkspaceConfigurationFile(folderRelativePath: string): boolean {
|
|
||||||
return [WORKSPACE_CONFIG_DEFAULT_PATH, WORKSPACE_STANDALONE_CONFIGURATIONS[TASKS_CONFIGURATION_KEY], WORKSPACE_STANDALONE_CONFIGURATIONS[LAUNCH_CONFIGURATION_KEY]].some(p => p === folderRelativePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
private toResource(folderRelativePath: string): URI {
|
|
||||||
if (typeof folderRelativePath === 'string') {
|
|
||||||
return URI.file(paths.join(this.folder.fsPath, folderRelativePath));
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private toFolderRelativePath(resource: URI, toOSPath?: boolean): string {
|
|
||||||
if (this.contains(resource)) {
|
|
||||||
return paths.normalize(paths.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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// node.hs helper functions
|
|
||||||
|
|
||||||
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)) }; })
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IExportedConfigurationNode {
|
interface IExportedConfigurationNode {
|
||||||
name: string;
|
name: string;
|
||||||
description: string;
|
description: string;
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import { TPromise } from 'vs/base/common/winjs.base';
|
import { TPromise } from 'vs/base/common/winjs.base';
|
||||||
import URI from 'vs/base/common/uri';
|
|
||||||
import { createDecorator, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation';
|
import { createDecorator, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation';
|
||||||
import { IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
|
import { IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
|
||||||
|
|
||||||
@@ -15,16 +14,6 @@ export interface IWorkspaceEditingService {
|
|||||||
|
|
||||||
_serviceBrand: ServiceIdentifier<any>;
|
_serviceBrand: ServiceIdentifier<any>;
|
||||||
|
|
||||||
/**
|
|
||||||
* add folders to the existing workspace
|
|
||||||
*/
|
|
||||||
addFolders(folders: URI[]): TPromise<void>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* remove folders from the existing workspace
|
|
||||||
*/
|
|
||||||
removeFolders(folders: URI[]): TPromise<void>;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* creates a new workspace with the provided folders and opens it. if path is provided
|
* creates a new workspace with the provided folders and opens it. if path is provided
|
||||||
* the workspace will be saved into that location.
|
* the workspace will be saved into that location.
|
||||||
|
|||||||
@@ -9,14 +9,10 @@ import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common
|
|||||||
import URI from 'vs/base/common/uri';
|
import URI from 'vs/base/common/uri';
|
||||||
import { TPromise } from 'vs/base/common/winjs.base';
|
import { TPromise } from 'vs/base/common/winjs.base';
|
||||||
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
|
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
|
||||||
import { IWindowsService, IWindowService, IEnterWorkspaceResult } from 'vs/platform/windows/common/windows';
|
import { IWindowService, IEnterWorkspaceResult } from 'vs/platform/windows/common/windows';
|
||||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
|
||||||
import { IJSONEditingService } from 'vs/workbench/services/configuration/common/jsonEditing';
|
import { IJSONEditingService } from 'vs/workbench/services/configuration/common/jsonEditing';
|
||||||
import { IWorkspacesService, IStoredWorkspaceFolder, IWorkspaceIdentifier, isStoredWorkspaceFolder } from 'vs/platform/workspaces/common/workspaces';
|
import { IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
|
||||||
import { dirname } from 'path';
|
|
||||||
import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration';
|
import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration';
|
||||||
import { massageFolderPathForWorkspace } from 'vs/platform/workspaces/node/workspaces';
|
|
||||||
import { isLinux } from 'vs/base/common/platform';
|
|
||||||
import { WorkspaceService } from 'vs/workbench/services/configuration/node/configurationService';
|
import { WorkspaceService } from 'vs/workbench/services/configuration/node/configurationService';
|
||||||
import { migrateStorageToMultiRootWorkspace } from 'vs/platform/storage/common/migration';
|
import { migrateStorageToMultiRootWorkspace } from 'vs/platform/storage/common/migration';
|
||||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||||
@@ -26,7 +22,6 @@ import { Registry } from 'vs/platform/registry/common/platform';
|
|||||||
import { IExtensionService } from 'vs/platform/extensions/common/extensions';
|
import { IExtensionService } from 'vs/platform/extensions/common/extensions';
|
||||||
import { IBackupFileService } from 'vs/workbench/services/backup/common/backup';
|
import { IBackupFileService } from 'vs/workbench/services/backup/common/backup';
|
||||||
import { BackupFileService } from 'vs/workbench/services/backup/node/backupFileService';
|
import { BackupFileService } from 'vs/workbench/services/backup/node/backupFileService';
|
||||||
import { Schemas } from 'vs/base/common/network';
|
|
||||||
|
|
||||||
export class WorkspaceEditingService implements IWorkspaceEditingService {
|
export class WorkspaceEditingService implements IWorkspaceEditingService {
|
||||||
|
|
||||||
@@ -35,10 +30,7 @@ export class WorkspaceEditingService implements IWorkspaceEditingService {
|
|||||||
constructor(
|
constructor(
|
||||||
@IJSONEditingService private jsonEditingService: IJSONEditingService,
|
@IJSONEditingService private jsonEditingService: IJSONEditingService,
|
||||||
@IWorkspaceContextService private contextService: WorkspaceService,
|
@IWorkspaceContextService private contextService: WorkspaceService,
|
||||||
@IEnvironmentService private environmentService: IEnvironmentService,
|
|
||||||
@IWindowsService private windowsService: IWindowsService,
|
|
||||||
@IWindowService private windowService: IWindowService,
|
@IWindowService private windowService: IWindowService,
|
||||||
@IWorkspacesService private workspacesService: IWorkspacesService,
|
|
||||||
@IWorkspaceConfigurationService private workspaceConfigurationService: IWorkspaceConfigurationService,
|
@IWorkspaceConfigurationService private workspaceConfigurationService: IWorkspaceConfigurationService,
|
||||||
@IStorageService private storageService: IStorageService,
|
@IStorageService private storageService: IStorageService,
|
||||||
@IExtensionService private extensionService: IExtensionService,
|
@IExtensionService private extensionService: IExtensionService,
|
||||||
@@ -46,89 +38,6 @@ export class WorkspaceEditingService implements IWorkspaceEditingService {
|
|||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
public addFolders(foldersToAdd: URI[]): TPromise<void> {
|
|
||||||
if (!this.isSupported()) {
|
|
||||||
return TPromise.as(void 0); // we need a workspace to begin with
|
|
||||||
}
|
|
||||||
|
|
||||||
const currentWorkspaceFolders = this.contextService.getWorkspace().folders;
|
|
||||||
const currentWorkspaceFolderUris = currentWorkspaceFolders.map(folder => folder.uri);
|
|
||||||
const currentStoredFolders = currentWorkspaceFolders.map(folder => folder.raw);
|
|
||||||
|
|
||||||
const storedFoldersToAdd: IStoredWorkspaceFolder[] = [];
|
|
||||||
|
|
||||||
const workspaceConfigFolder = dirname(this.contextService.getWorkspace().configuration.fsPath);
|
|
||||||
|
|
||||||
foldersToAdd.forEach(folderToAdd => {
|
|
||||||
if (this.contains(currentWorkspaceFolderUris, folderToAdd)) {
|
|
||||||
return; // already existing
|
|
||||||
}
|
|
||||||
|
|
||||||
// File resource: use "path" property
|
|
||||||
if (folderToAdd.scheme === Schemas.file) {
|
|
||||||
storedFoldersToAdd.push({
|
|
||||||
path: massageFolderPathForWorkspace(folderToAdd.fsPath, workspaceConfigFolder, currentStoredFolders)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Any other resource: use "uri" property
|
|
||||||
else {
|
|
||||||
storedFoldersToAdd.push({
|
|
||||||
uri: folderToAdd.toString(true)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (storedFoldersToAdd.length > 0) {
|
|
||||||
return this.doSetFolders([...currentStoredFolders, ...storedFoldersToAdd]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return TPromise.as(void 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public removeFolders(foldersToRemove: URI[]): TPromise<void> {
|
|
||||||
if (!this.isSupported()) {
|
|
||||||
return TPromise.as(void 0); // we need a workspace to begin with
|
|
||||||
}
|
|
||||||
|
|
||||||
const currentWorkspaceFolders = this.contextService.getWorkspace().folders;
|
|
||||||
const currentStoredFolders = currentWorkspaceFolders.map(folder => folder.raw);
|
|
||||||
|
|
||||||
const newStoredFolders: IStoredWorkspaceFolder[] = currentStoredFolders.filter((folder, index) => {
|
|
||||||
if (!isStoredWorkspaceFolder(folder)) {
|
|
||||||
return true; // keep entries which are unrelated
|
|
||||||
}
|
|
||||||
|
|
||||||
return !this.contains(foldersToRemove, currentWorkspaceFolders[index].uri); // keep entries which are unrelated
|
|
||||||
});
|
|
||||||
|
|
||||||
if (newStoredFolders.length !== currentStoredFolders.length) {
|
|
||||||
return this.doSetFolders(newStoredFolders);
|
|
||||||
}
|
|
||||||
|
|
||||||
return TPromise.as(void 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
private doSetFolders(folders: IStoredWorkspaceFolder[]): TPromise<void> {
|
|
||||||
const workspace = this.contextService.getWorkspace();
|
|
||||||
|
|
||||||
return this.jsonEditingService.write(workspace.configuration, { key: 'folders', value: folders }, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private isSupported(): boolean {
|
|
||||||
return this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE; // we need a multi folder workspace to begin with;
|
|
||||||
}
|
|
||||||
|
|
||||||
private contains(resources: URI[], toCheck: URI): boolean {
|
|
||||||
return resources.some(resource => {
|
|
||||||
if (isLinux) {
|
|
||||||
return resource.toString() === toCheck.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
return resource.toString().toLowerCase() === toCheck.toString().toLowerCase();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public createAndEnterWorkspace(folderPaths?: string[], path?: string): TPromise<void> {
|
public createAndEnterWorkspace(folderPaths?: string[], path?: string): TPromise<void> {
|
||||||
return this.doEnterWorkspace(() => this.windowService.createAndEnterWorkspace(folderPaths, path));
|
return this.doEnterWorkspace(() => this.windowService.createAndEnterWorkspace(folderPaths, path));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -118,6 +118,14 @@ export class TestContextService implements IWorkspaceContextService {
|
|||||||
return this.workspace;
|
return this.workspace;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public addFolders(foldersToAdd: URI[]): TPromise<void> {
|
||||||
|
return TPromise.as(void 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public removeFolders(foldersToRemove: URI[]): TPromise<void> {
|
||||||
|
return TPromise.as(void 0);
|
||||||
|
}
|
||||||
|
|
||||||
public getWorkspaceFolder(resource: URI): IWorkspaceFolder {
|
public getWorkspaceFolder(resource: URI): IWorkspaceFolder {
|
||||||
return this.isInsideWorkspace(resource) ? this.workspace.folders[0] : null;
|
return this.isInsideWorkspace(resource) ? this.workspace.folders[0] : null;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user