Better variable resolve on remote

Part of microsoft/vscode-remote-release#2619
This commit is contained in:
Alex Ross
2020-09-15 13:30:34 +02:00
parent 2ccce8200b
commit 49ec999d80
5 changed files with 66 additions and 27 deletions
@@ -1032,7 +1032,7 @@ export class ExtHostVariableResolverService extends AbstractVariableResolverServ
}
return undefined;
}
}, env, !editorService);
}, undefined, env, !editorService);
}
}
@@ -5,7 +5,6 @@
import { URI as uri } from 'vs/base/common/uri';
import * as nls from 'vs/nls';
import * as path from 'vs/base/common/path';
import * as Types from 'vs/base/common/types';
import { Schemas } from 'vs/base/common/network';
import { toResource } from 'vs/workbench/common/editor';
@@ -22,6 +21,7 @@ import { IQuickInputService, IInputOptions, IQuickPickItem, IPickOptions } from
import { ConfiguredInput, IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver';
import { IProcessEnvironment } from 'vs/base/common/platform';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { ILabelService } from 'vs/platform/label/common/label';
export abstract class BaseConfigurationResolverService extends AbstractVariableResolverService {
@@ -34,7 +34,8 @@ export abstract class BaseConfigurationResolverService extends AbstractVariableR
private readonly configurationService: IConfigurationService,
private readonly commandService: ICommandService,
private readonly workspaceContextService: IWorkspaceContextService,
private readonly quickInputService: IQuickInputService
private readonly quickInputService: IQuickInputService,
private readonly labelService: ILabelService
) {
super({
getFolderUri: (folderName: string): uri | undefined => {
@@ -55,11 +56,11 @@ export abstract class BaseConfigurationResolverService extends AbstractVariableR
if (activeEditor instanceof DiffEditorInput) {
activeEditor = activeEditor.modifiedInput;
}
const fileResource = toResource(activeEditor, { filterByScheme: [Schemas.file, Schemas.userData] });
const fileResource = toResource(activeEditor, { filterByScheme: [Schemas.file, Schemas.userData, Schemas.vscodeRemote] });
if (!fileResource) {
return undefined;
}
return path.normalize(fileResource.fsPath);
return this.labelService.getUriLabel(fileResource);
},
getSelectedText: (): string | undefined => {
const activeTextEditorControl = editorService.activeTextEditorControl;
@@ -83,7 +84,7 @@ export abstract class BaseConfigurationResolverService extends AbstractVariableR
}
return undefined;
}
}, envVariables);
}, labelService, envVariables);
}
public async resolveWithInteractionReplace(folder: IWorkspaceFolder | undefined, config: any, section?: string, variables?: IStringDictionary<string>, target?: ConfigurationTarget): Promise<any> {
@@ -347,9 +348,10 @@ export class ConfigurationResolverService extends BaseConfigurationResolverServi
@IConfigurationService configurationService: IConfigurationService,
@ICommandService commandService: ICommandService,
@IWorkspaceContextService workspaceContextService: IWorkspaceContextService,
@IQuickInputService quickInputService: IQuickInputService
@IQuickInputService quickInputService: IQuickInputService,
@ILabelService labelService: ILabelService
) {
super({ getExecPath: () => undefined }, Object.create(null), editorService, configurationService, commandService, workspaceContextService, quickInputService);
super({ getExecPath: () => undefined }, Object.create(null), editorService, configurationService, commandService, workspaceContextService, quickInputService, labelService);
}
}
@@ -14,6 +14,7 @@ import { localize } from 'vs/nls';
import { URI as uri } from 'vs/base/common/uri';
import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver';
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { ILabelService } from 'vs/platform/label/common/label';
export interface IVariableResolveContext {
getFolderUri(folderName: string): uri | undefined;
@@ -32,12 +33,14 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe
declare readonly _serviceBrand: undefined;
private _context: IVariableResolveContext;
private _labelService?: ILabelService;
private _envVariables?: IProcessEnvironment;
protected _contributedVariables: Map<string, () => Promise<string | undefined>> = new Map();
constructor(_context: IVariableResolveContext, _envVariables?: IProcessEnvironment, private _ignoreEditorVariables = false) {
constructor(_context: IVariableResolveContext, _labelService?: ILabelService, _envVariables?: IProcessEnvironment, private _ignoreEditorVariables = false) {
this._context = _context;
this._labelService = _labelService;
if (_envVariables) {
if (isWindows) {
// windows env variables are case insensitive
@@ -140,6 +143,10 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe
return replaced;
}
private fsPath(displayUri: uri): string {
return this._labelService ? this._labelService.getUriLabel(displayUri) : displayUri.fsPath;
}
private evaluateSingleVariable(match: string, variable: string, folderUri: uri | undefined, commandValueMapping: IStringDictionary<string> | undefined): string {
// try to separate variable arguments from variable name
@@ -221,14 +228,14 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe
switch (variable) {
case 'workspaceRoot':
case 'workspaceFolder':
return normalizeDriveLetter(getFolderUri().fsPath);
return normalizeDriveLetter(this.fsPath(getFolderUri()));
case 'cwd':
return ((folderUri || argument) ? normalizeDriveLetter(getFolderUri().fsPath) : process.cwd());
return ((folderUri || argument) ? normalizeDriveLetter(this.fsPath(getFolderUri())) : process.cwd());
case 'workspaceRootFolderName':
case 'workspaceFolderBasename':
return paths.basename(getFolderUri().fsPath);
return paths.basename(this.fsPath(getFolderUri()));
case 'lineNumber':
if (this._ignoreEditorVariables) {
@@ -261,7 +268,7 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe
return match;
}
if (folderUri || argument) {
return paths.normalize(paths.relative(getFolderUri().fsPath, getFilePath()));
return paths.relative(this.fsPath(getFolderUri()), getFilePath());
}
return getFilePath();
@@ -271,7 +278,7 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe
}
const dirname = paths.dirname(getFilePath());
if (folderUri || argument) {
return paths.normalize(paths.relative(getFolderUri().fsPath, dirname));
return paths.relative(this.fsPath(getFolderUri()), dirname);
}
return dirname;
@@ -15,6 +15,7 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { IProcessEnvironment } from 'vs/base/common/platform';
import { BaseConfigurationResolverService } from 'vs/workbench/services/configurationResolver/browser/configurationResolverService';
import { process } from 'vs/base/parts/sandbox/electron-sandbox/globals';
import { ILabelService } from 'vs/platform/label/common/label';
export class ConfigurationResolverService extends BaseConfigurationResolverService {
@@ -24,13 +25,14 @@ export class ConfigurationResolverService extends BaseConfigurationResolverServi
@IConfigurationService configurationService: IConfigurationService,
@ICommandService commandService: ICommandService,
@IWorkspaceContextService workspaceContextService: IWorkspaceContextService,
@IQuickInputService quickInputService: IQuickInputService
@IQuickInputService quickInputService: IQuickInputService,
@ILabelService labelService: ILabelService
) {
super({
getExecPath: (): string | undefined => {
return environmentService.execPath;
}
}, process.env as IProcessEnvironment, editorService, configurationService, commandService, workspaceContextService, quickInputService);
}, process.env as IProcessEnvironment, editorService, configurationService, commandService, workspaceContextService, quickInputService, labelService);
}
}
@@ -4,18 +4,19 @@
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import { Event } from 'vs/base/common/event';
import * as path from 'path';
import { Emitter, Event } from 'vs/base/common/event';
import { URI as uri } from 'vs/base/common/uri';
import * as platform from 'vs/base/common/platform';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver';
import { BaseConfigurationResolverService } from 'vs/workbench/services/configurationResolver/browser/configurationResolverService';
import { Workspace, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { Workspace, IWorkspaceFolder, IWorkspace } from 'vs/platform/workspace/common/workspace';
import { TestEditorService } from 'vs/workbench/test/browser/workbenchTestServices';
import { TestWorkbenchConfiguration } from 'vs/workbench/test/electron-browser/workbenchTestServices';
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
import { Disposable } from 'vs/base/common/lifecycle';
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
import { IQuickInputService, IQuickPickItem, QuickPickInput, IPickOptions, Omit, IInputOptions, IQuickInputButton, IQuickPick, IInputBox, IQuickNavigateConfiguration } from 'vs/platform/quickinput/common/quickInput';
import { CancellationToken } from 'vs/base/common/cancellation';
import * as Types from 'vs/base/common/types';
@@ -24,6 +25,8 @@ import { Selection } from 'vs/editor/common/core/selection';
import { NativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-browser/environmentService';
import { TestContextService } from 'vs/workbench/test/common/workbenchTestServices';
import { testWorkspace } from 'vs/platform/workspace/test/common/testWorkspace';
import { IFormatterChangeEvent, ILabelService, ResourceLabelFormatter } from 'vs/platform/label/common/label';
import { IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
const mockLineNumber = 10;
class TestEditorServiceWithActiveEditor extends TestEditorService {
@@ -59,15 +62,17 @@ suite('Configuration Resolver Service', () => {
let containingWorkspace: Workspace;
let workspace: IWorkspaceFolder;
let quickInputService: MockQuickInputService;
let labelService: MockLabelService;
setup(() => {
mockCommandService = new MockCommandService();
editorService = new TestEditorServiceWithActiveEditor();
quickInputService = new MockQuickInputService();
environmentService = new MockWorkbenchEnvironmentService(envVariables);
labelService = new MockLabelService();
containingWorkspace = testWorkspace(uri.parse('file:///VSCode/workspaceLocation'));
workspace = containingWorkspace.folders[0];
configurationResolverService = new TestConfigurationResolverService({ getExecPath: () => undefined }, environmentService.userEnv, editorService, new MockInputsConfigurationService(), mockCommandService, new TestContextService(containingWorkspace), quickInputService);
configurationResolverService = new TestConfigurationResolverService({ getExecPath: () => undefined }, environmentService.userEnv, editorService, new MockInputsConfigurationService(), mockCommandService, new TestContextService(containingWorkspace), quickInputService, labelService);
});
teardown(() => {
@@ -202,7 +207,7 @@ suite('Configuration Resolver Service', () => {
}
});
let service = new TestConfigurationResolverService({ getExecPath: () => undefined }, environmentService.userEnv, new TestEditorServiceWithActiveEditor(), configurationService, mockCommandService, new TestContextService(), quickInputService);
let service = new TestConfigurationResolverService({ getExecPath: () => undefined }, environmentService.userEnv, new TestEditorServiceWithActiveEditor(), configurationService, mockCommandService, new TestContextService(), quickInputService, labelService);
assert.strictEqual(service.resolve(workspace, 'abc ${config:editor.fontFamily} xyz'), 'abc foo xyz');
});
@@ -219,7 +224,7 @@ suite('Configuration Resolver Service', () => {
}
});
let service = new TestConfigurationResolverService({ getExecPath: () => undefined }, environmentService.userEnv, new TestEditorServiceWithActiveEditor(), configurationService, mockCommandService, new TestContextService(), quickInputService);
let service = new TestConfigurationResolverService({ getExecPath: () => undefined }, environmentService.userEnv, new TestEditorServiceWithActiveEditor(), configurationService, mockCommandService, new TestContextService(), quickInputService, labelService);
assert.strictEqual(service.resolve(workspace, 'abc ${config:editor.fontFamily} ${config:terminal.integrated.fontFamily} xyz'), 'abc foo bar xyz');
});
@@ -236,7 +241,7 @@ suite('Configuration Resolver Service', () => {
}
});
let service = new TestConfigurationResolverService({ getExecPath: () => undefined }, environmentService.userEnv, new TestEditorServiceWithActiveEditor(), configurationService, mockCommandService, new TestContextService(), quickInputService);
let service = new TestConfigurationResolverService({ getExecPath: () => undefined }, environmentService.userEnv, new TestEditorServiceWithActiveEditor(), configurationService, mockCommandService, new TestContextService(), quickInputService, labelService);
if (platform.isWindows) {
assert.strictEqual(service.resolve(workspace, 'abc ${config:editor.fontFamily} ${workspaceFolder} ${env:key1} xyz'), 'abc foo \\VSCode\\workspaceLocation Value for key1 xyz');
} else {
@@ -257,7 +262,7 @@ suite('Configuration Resolver Service', () => {
}
});
let service = new TestConfigurationResolverService({ getExecPath: () => undefined }, environmentService.userEnv, new TestEditorServiceWithActiveEditor(), configurationService, mockCommandService, new TestContextService(), quickInputService);
let service = new TestConfigurationResolverService({ getExecPath: () => undefined }, environmentService.userEnv, new TestEditorServiceWithActiveEditor(), configurationService, mockCommandService, new TestContextService(), quickInputService, labelService);
if (platform.isWindows) {
assert.strictEqual(service.resolve(workspace, '${config:editor.fontFamily} ${config:terminal.integrated.fontFamily} ${workspaceFolder} - ${workspaceFolder} ${env:key1} - ${env:key2}'), 'foo bar \\VSCode\\workspaceLocation - \\VSCode\\workspaceLocation Value for key1 - Value for key2');
} else {
@@ -291,7 +296,7 @@ suite('Configuration Resolver Service', () => {
}
});
let service = new TestConfigurationResolverService({ getExecPath: () => undefined }, environmentService.userEnv, new TestEditorServiceWithActiveEditor(), configurationService, mockCommandService, new TestContextService(), quickInputService);
let service = new TestConfigurationResolverService({ getExecPath: () => undefined }, environmentService.userEnv, new TestEditorServiceWithActiveEditor(), configurationService, mockCommandService, new TestContextService(), quickInputService, labelService);
assert.strictEqual(service.resolve(workspace, 'abc ${config:editor.fontFamily} ${config:editor.lineNumbers} ${config:editor.insertSpaces} xyz'), 'abc foo 123 false xyz');
});
@@ -301,7 +306,7 @@ suite('Configuration Resolver Service', () => {
editor: {}
});
let service = new TestConfigurationResolverService({ getExecPath: () => undefined }, environmentService.userEnv, new TestEditorServiceWithActiveEditor(), configurationService, mockCommandService, new TestContextService(), quickInputService);
let service = new TestConfigurationResolverService({ getExecPath: () => undefined }, environmentService.userEnv, new TestEditorServiceWithActiveEditor(), configurationService, mockCommandService, new TestContextService(), quickInputService, labelService);
assert.strictEqual(service.resolve(workspace, 'abc ${unknownVariable} xyz'), 'abc ${unknownVariable} xyz');
assert.strictEqual(service.resolve(workspace, 'abc ${env:unknownVariable} xyz'), 'abc xyz');
});
@@ -314,7 +319,7 @@ suite('Configuration Resolver Service', () => {
}
});
let service = new TestConfigurationResolverService({ getExecPath: () => undefined }, environmentService.userEnv, new TestEditorServiceWithActiveEditor(), configurationService, mockCommandService, new TestContextService(), quickInputService);
let service = new TestConfigurationResolverService({ getExecPath: () => undefined }, environmentService.userEnv, new TestEditorServiceWithActiveEditor(), configurationService, mockCommandService, new TestContextService(), quickInputService, labelService);
assert.throws(() => service.resolve(workspace, 'abc ${env} xyz'));
assert.throws(() => service.resolve(workspace, 'abc ${env:} xyz'));
@@ -647,6 +652,29 @@ class MockQuickInputService implements IQuickInputService {
}
}
class MockLabelService implements ILabelService {
_serviceBrand: undefined;
getUriLabel(resource: uri, options?: { relative?: boolean | undefined; noPrefix?: boolean | undefined; endWithSeparator?: boolean | undefined; }): string {
return path.normalize(resource.fsPath);
}
getUriBasenameLabel(resource: uri): string {
throw new Error('Method not implemented.');
}
getWorkspaceLabel(workspace: uri | IWorkspaceIdentifier | IWorkspace, options?: { verbose: boolean; }): string {
throw new Error('Method not implemented.');
}
getHostLabel(scheme: string, authority?: string): string {
throw new Error('Method not implemented.');
}
getSeparator(scheme: string, authority?: string): '/' | '\\' {
throw new Error('Method not implemented.');
}
registerFormatter(formatter: ResourceLabelFormatter): IDisposable {
throw new Error('Method not implemented.');
}
onDidChangeFormatters: Event<IFormatterChangeEvent> = new Emitter<IFormatterChangeEvent>().event;
}
class MockInputsConfigurationService extends TestConfigurationService {
public getValue(arg1?: any, arg2?: any): any {
let configuration;