Merge VS Code 1.23.1 (#1520)

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

View File

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