diff --git a/src/vs/workbench/api/common/extHostDebugService.ts b/src/vs/workbench/api/common/extHostDebugService.ts index b82f72a7a48..bf17ce57b2e 100644 --- a/src/vs/workbench/api/common/extHostDebugService.ts +++ b/src/vs/workbench/api/common/extHostDebugService.ts @@ -105,7 +105,7 @@ export abstract class ExtHostDebugServiceBase implements IExtHostDebugService, E constructor( @IExtHostRpcService extHostRpcService: IExtHostRpcService, - @IExtHostWorkspace private _workspaceService: IExtHostWorkspace, + @IExtHostWorkspace protected _workspaceService: IExtHostWorkspace, @IExtHostExtensionService private _extensionService: IExtHostExtensionService, @IExtHostDocumentsAndEditors private _editorsService: IExtHostDocumentsAndEditors, @IExtHostConfiguration protected _configurationService: IExtHostConfiguration, @@ -930,7 +930,7 @@ export class ExtHostDebugConsole implements vscode.DebugConsole { export class ExtHostVariableResolverService extends AbstractVariableResolverService { - constructor(folders: vscode.WorkspaceFolder[], editorService: ExtHostDocumentsAndEditors | undefined, configurationService: ExtHostConfigProvider, env?: IProcessEnvironment) { + constructor(folders: vscode.WorkspaceFolder[], editorService: ExtHostDocumentsAndEditors | undefined, configurationService: ExtHostConfigProvider, env?: IProcessEnvironment, workspaceService?: IExtHostWorkspace) { super({ getFolderUri: (folderName: string): URI | undefined => { const found = folders.filter(f => f.name === folderName); @@ -957,6 +957,18 @@ export class ExtHostVariableResolverService extends AbstractVariableResolverServ } return undefined; }, + getWorkspaceFolderPathForFile: (): string | undefined => { + if (editorService && workspaceService) { + const activeEditor = editorService.activeEditor(); + if (activeEditor) { + const ws = workspaceService.getWorkspaceFolder(activeEditor.document.uri); + if (ws) { + return path.normalize(ws.uri.fsPath); + } + } + } + return undefined; + }, getSelectedText: (): string | undefined => { if (editorService) { const activeEditor = editorService.activeEditor(); @@ -975,7 +987,7 @@ export class ExtHostVariableResolverService extends AbstractVariableResolverServ } return undefined; } - }, undefined, env, !editorService); + }, undefined, env); } } diff --git a/src/vs/workbench/api/node/extHostDebugService.ts b/src/vs/workbench/api/node/extHostDebugService.ts index 9c57a44248e..32573ee46c7 100644 --- a/src/vs/workbench/api/node/extHostDebugService.ts +++ b/src/vs/workbench/api/node/extHostDebugService.ts @@ -122,7 +122,7 @@ export class ExtHostDebugService extends ExtHostDebugServiceBase { } protected createVariableResolver(folders: vscode.WorkspaceFolder[], editorService: ExtHostDocumentsAndEditors, configurationService: ExtHostConfigProvider): AbstractVariableResolverService { - return new ExtHostVariableResolverService(folders, editorService, configurationService, process.env as env.IProcessEnvironment); + return new ExtHostVariableResolverService(folders, editorService, configurationService, process.env as env.IProcessEnvironment, this._workspaceService); } } diff --git a/src/vs/workbench/services/configurationResolver/browser/configurationResolverService.ts b/src/vs/workbench/services/configurationResolver/browser/configurationResolverService.ts index a40d034ec7f..87794e99a19 100644 --- a/src/vs/workbench/services/configurationResolver/browser/configurationResolverService.ts +++ b/src/vs/workbench/services/configurationResolver/browser/configurationResolverService.ts @@ -60,6 +60,20 @@ export abstract class BaseConfigurationResolverService extends AbstractVariableR } return this.labelService.getUriLabel(fileResource, { noPrefix: true }); }, + getWorkspaceFolderPathForFile: (): string | undefined => { + const fileResource = EditorResourceAccessor.getOriginalUri(editorService.activeEditor, { + supportSideBySide: SideBySideEditor.PRIMARY, + filterByScheme: [Schemas.file, Schemas.userData, Schemas.vscodeRemote] + }); + if (!fileResource) { + return undefined; + } + const wsFolder = workspaceContextService.getWorkspaceFolder(fileResource); + if (!wsFolder) { + return undefined; + } + return this.labelService.getUriLabel(wsFolder.uri, { noPrefix: true }); + }, getSelectedText: (): string | undefined => { const activeTextEditorControl = editorService.activeTextEditorControl; if (isCodeEditor(activeTextEditorControl)) { diff --git a/src/vs/workbench/services/configurationResolver/common/variableResolver.ts b/src/vs/workbench/services/configurationResolver/common/variableResolver.ts index f342ea2c1fe..58ef6258ea4 100644 --- a/src/vs/workbench/services/configurationResolver/common/variableResolver.ts +++ b/src/vs/workbench/services/configurationResolver/common/variableResolver.ts @@ -22,6 +22,7 @@ export interface IVariableResolveContext { getConfigurationValue(folderUri: uri, section: string): string | undefined; getExecPath(): string | undefined; getFilePath(): string | undefined; + getWorkspaceFolderPathForFile?(): string | undefined; getSelectedText(): string | undefined; getLineNumber(): string | undefined; } @@ -39,7 +40,7 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe protected _contributedVariables: Map Promise> = new Map(); - constructor(_context: IVariableResolveContext, _labelService?: ILabelService, _envVariables?: IProcessEnvironment, private _ignoreEditorVariables = false) { + constructor(_context: IVariableResolveContext, _labelService?: ILabelService, _envVariables?: IProcessEnvironment) { this._context = _context; this._labelService = _labelService; if (_envVariables) { @@ -169,7 +170,20 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe if (filePath) { return filePath; } - throw new Error(localize('canNotResolveFile', "'{0}' can not be resolved. Please open an editor.", match)); + throw new Error(localize('canNotResolveFile', "Variable {0} can not be resolved. Please open an editor.", match)); + }; + + // common error handling for all variables that require an open editor + const getFolderPathForFile = (): string => { + + const filePath = getFilePath(); // throws error if no editor open + if (this._context.getWorkspaceFolderPathForFile) { + const folderPath = this._context.getWorkspaceFolderPathForFile(); + if (folderPath) { + return folderPath; + } + } + throw new Error(localize('canNotResolveFolderForFile', "Variable {0}: can not find workspace folder of '{1}'.", match, paths.basename(filePath))); }; // common error handling for all variables that require an open folder and accept a folder name argument @@ -180,7 +194,7 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe if (folder) { return folder; } - throw new Error(localize('canNotFindFolder', "'{0}' can not be resolved. No such folder '{1}'.", match, argument)); + throw new Error(localize('canNotFindFolder', "Variable {0} can not be resolved. No such folder '{1}'.", match, argument)); } if (folderUri) { @@ -188,9 +202,9 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe } if (this._context.getWorkspaceFolderCount() > 1) { - throw new Error(localize('canNotResolveWorkspaceFolderMultiRoot', "'{0}' can not be resolved in a multi folder workspace. Scope this variable using ':' and a workspace folder name.", match)); + throw new Error(localize('canNotResolveWorkspaceFolderMultiRoot', "Variable {0} can not be resolved in a multi folder workspace. Scope this variable using ':' and a workspace folder name.", match)); } - throw new Error(localize('canNotResolveWorkspaceFolder', "'{0}' can not be resolved. Please open a folder.", match)); + throw new Error(localize('canNotResolveWorkspaceFolder', "Variable {0} can not be resolved. Please open a folder.", match)); }; @@ -207,20 +221,20 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe // For `env` we should do the same as a normal shell does - evaluates undefined envs to an empty string #46436 return ''; } - throw new Error(localize('missingEnvVarName', "'{0}' can not be resolved because no environment variable name is given.", match)); + throw new Error(localize('missingEnvVarName', "Variable {0} can not be resolved because no environment variable name is given.", match)); case 'config': if (argument) { const config = this._context.getConfigurationValue(getFolderUri(false), argument); if (types.isUndefinedOrNull(config)) { - throw new Error(localize('configNotFound', "'{0}' can not be resolved because setting '{1}' not found.", match, argument)); + throw new Error(localize('configNotFound', "Variable {0} can not be resolved because setting '{1}' not found.", match, argument)); } if (types.isObject(config)) { - throw new Error(localize('configNoString', "'{0}' can not be resolved because '{1}' is a structured value.", match, argument)); + throw new Error(localize('configNoString', "Variable {0} can not be resolved because '{1}' is a structured value.", match, argument)); } return config; } - throw new Error(localize('missingConfigName', "'{0}' can not be resolved because no settings name is given.", match)); + throw new Error(localize('missingConfigName', "Variable {0} can not be resolved because no settings name is given.", match)); case 'command': return this.resolveFromMap(match, argument, commandValueMapping, 'command'); @@ -243,44 +257,32 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe return paths.basename(this.fsPath(getFolderUri())); case 'lineNumber': - if (this._ignoreEditorVariables) { - return match; - } const lineNumber = this._context.getLineNumber(); if (lineNumber) { return lineNumber; } - throw new Error(localize('canNotResolveLineNumber', "'{0}' can not be resolved. Make sure to have a line selected in the active editor.", match)); + throw new Error(localize('canNotResolveLineNumber', "Variable {0} can not be resolved. Make sure to have a line selected in the active editor.", match)); case 'selectedText': - if (this._ignoreEditorVariables) { - return match; - } const selectedText = this._context.getSelectedText(); if (selectedText) { return selectedText; } - throw new Error(localize('canNotResolveSelectedText', "'{0}' can not be resolved. Make sure to have some text selected in the active editor.", match)); + throw new Error(localize('canNotResolveSelectedText', "Variable {0} can not be resolved. Make sure to have some text selected in the active editor.", match)); case 'file': - if (this._ignoreEditorVariables) { - return match; - } return getFilePath(); + case 'fileWorkspaceFolder': + return getFolderPathForFile(); + case 'relativeFile': - if (this._ignoreEditorVariables) { - return match; - } if (folderUri || argument) { return paths.relative(this.fsPath(getFolderUri()), getFilePath()); } return getFilePath(); case 'relativeFileDirname': - if (this._ignoreEditorVariables) { - return match; - } const dirname = paths.dirname(getFilePath()); if (folderUri || argument) { const relative = paths.relative(this.fsPath(getFolderUri()), dirname); @@ -289,34 +291,19 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe return dirname; case 'fileDirname': - if (this._ignoreEditorVariables) { - return match; - } return paths.dirname(getFilePath()); case 'fileExtname': - if (this._ignoreEditorVariables) { - return match; - } return paths.extname(getFilePath()); case 'fileBasename': - if (this._ignoreEditorVariables) { - return match; - } return paths.basename(getFilePath()); case 'fileBasenameNoExtension': - if (this._ignoreEditorVariables) { - return match; - } const basename = paths.basename(getFilePath()); return (basename.slice(0, basename.length - paths.extname(basename).length)); case 'fileDirnameBasename': - if (this._ignoreEditorVariables) { - return match; - } return paths.basename(paths.dirname(getFilePath())); case 'execPath': @@ -346,7 +333,7 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe if (typeof v === 'string') { return v; } - throw new Error(localize('noValueForCommand', "'{0}' can not be resolved because the command has no value.", match)); + throw new Error(localize('noValueForCommand', "Variable {0} can not be resolved because the command has no value.", match)); } return match; }