mirror of
https://github.com/Microsoft/vscode
synced 2024-07-05 01:08:57 +00:00
variables: allow resolving extensionDir
(#146274)
* variables: allow resolving `extensionDir` This allows us to fix https://github.com/microsoft/vscode-remote-release/issues/5516#issuecomment-911597917 It enables a new replacement in the format `${extensionDir:<id>}` which will expand to the filesystem path where the extension is stored. This involved churn, since now resolution is always synchronous (where before the terminal took a synchronous-only path.) Additionally, changes were needed to inject this information in the variable resolver. As part of this I made the extension host resolver (used by debug and tasks) its own extension host service. * fixup! preserve object key order in resolution, add extensionDir support * fixup! address pr comments * fixup! address pr comments * fixup! address pr comments * config: fix config replacement only working for first variable per line * fixup! fix unit tests
This commit is contained in:
parent
0fba8139ce
commit
0de44f9786
|
@ -40,7 +40,7 @@ function registerVariableCompletions(pattern: string): vscode.Disposable {
|
|||
provideCompletionItems(document, position, _token) {
|
||||
const location = getLocation(document.getText(), document.offsetAt(position));
|
||||
if (!location.isAtPropertyKey && location.previousNode && location.previousNode.type === 'string') {
|
||||
const indexOf$ = document.lineAt(position.line).text.indexOf('$');
|
||||
const indexOf$ = document.lineAt(position.line).text.lastIndexOf('$', position.character);
|
||||
const startPosition = indexOf$ >= 0 ? new vscode.Position(position.line, indexOf$) : position;
|
||||
|
||||
return [
|
||||
|
@ -58,9 +58,11 @@ function registerVariableCompletions(pattern: string): vscode.Disposable {
|
|||
{ label: 'fileBasenameNoExtension', detail: localize('fileBasenameNoExtension', "The current opened file's basename with no file extension") },
|
||||
{ label: 'defaultBuildTask', detail: localize('defaultBuildTask', "The name of the default build task. If there is not a single default build task then a quick pick is shown to choose the build task.") },
|
||||
{ label: 'pathSeparator', detail: localize('pathSeparator', "The character used by the operating system to separate components in file paths") },
|
||||
{ label: 'extensionInstallFolder', detail: localize('extensionInstallFolder', "The path where an an extension is installed."), param: 'publisher.extension' },
|
||||
].map(variable => ({
|
||||
label: '${' + variable.label + '}',
|
||||
label: `\${${variable.label}}`,
|
||||
range: new vscode.Range(startPosition, position),
|
||||
insertText: variable.param ? new vscode.SnippetString(`\${${variable.label}:`).appendPlaceholder(variable.param).appendText('}') : (`\${${variable.label}}`),
|
||||
detail: variable.detail
|
||||
}));
|
||||
}
|
||||
|
|
|
@ -274,6 +274,29 @@ export function lastNonWhitespaceIndex(str: string, startIndex: number = str.len
|
|||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function that works identically to String.prototype.replace, except, the
|
||||
* replace function is allowed to be async and return a Promise.
|
||||
*/
|
||||
export function replaceAsync(str: string, search: RegExp, replacer: (match: string, ...args: any[]) => Promise<string>): Promise<string> {
|
||||
let parts: (string | Promise<string>)[] = [];
|
||||
|
||||
let last = 0;
|
||||
for (const match of str.matchAll(search)) {
|
||||
parts.push(str.slice(last, match.index));
|
||||
if (match.index === undefined) {
|
||||
throw new Error('match.index should be defined');
|
||||
}
|
||||
|
||||
last = match.index + match[0].length;
|
||||
parts.push(replacer(match[0], ...match.slice(1), match.index, str, match.groups));
|
||||
}
|
||||
|
||||
parts.push(str.slice(last));
|
||||
|
||||
return Promise.all(parts).then(p => p.join(''));
|
||||
}
|
||||
|
||||
export function compare(a: string, b: string): number {
|
||||
if (a < b) {
|
||||
return -1;
|
||||
|
|
|
@ -386,4 +386,13 @@ suite('Strings', () => {
|
|||
assert.strictEqual('hello world', strings.truncate('hello world', 100));
|
||||
assert.strictEqual('hello…', strings.truncate('hello world', 5));
|
||||
});
|
||||
|
||||
test('replaceAsync', async () => {
|
||||
let i = 0;
|
||||
assert.strictEqual(await strings.replaceAsync('abcabcabcabc', /b(.)/g, async (match, after) => {
|
||||
assert.strictEqual(match, 'bc');
|
||||
assert.strictEqual(after, 'c');
|
||||
return `${i++}${after}`;
|
||||
}), 'a0ca1ca2ca3c');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -29,13 +29,15 @@ import { AbstractVariableResolverService } from 'vs/workbench/services/configura
|
|||
import { buildUserEnvironment } from 'vs/server/node/extensionHostConnection';
|
||||
import { IServerEnvironmentService } from 'vs/server/node/serverEnvironmentService';
|
||||
import { IProductService } from 'vs/platform/product/common/productService';
|
||||
import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
|
||||
class CustomVariableResolver extends AbstractVariableResolverService {
|
||||
constructor(
|
||||
env: platform.IProcessEnvironment,
|
||||
workspaceFolders: IWorkspaceFolder[],
|
||||
activeFileResource: URI | undefined,
|
||||
resolvedVariables: { [name: string]: string }
|
||||
resolvedVariables: { [name: string]: string },
|
||||
extensionService: IExtensionManagementService,
|
||||
) {
|
||||
super({
|
||||
getFolderUri: (folderName: string): URI | undefined => {
|
||||
|
@ -68,7 +70,12 @@ class CustomVariableResolver extends AbstractVariableResolverService {
|
|||
},
|
||||
getLineNumber: (): string | undefined => {
|
||||
return resolvedVariables['lineNumber'];
|
||||
}
|
||||
},
|
||||
getExtension: async id => {
|
||||
const installed = await extensionService.getInstalled();
|
||||
const found = installed.find(e => e.identifier.id === id);
|
||||
return found && { extensionLocation: found.location };
|
||||
},
|
||||
}, undefined, Promise.resolve(os.homedir()), Promise.resolve(env));
|
||||
}
|
||||
}
|
||||
|
@ -89,7 +96,8 @@ export class RemoteTerminalChannel extends Disposable implements IServerChannel<
|
|||
private readonly _environmentService: IServerEnvironmentService,
|
||||
private readonly _logService: ILogService,
|
||||
private readonly _ptyService: IPtyService,
|
||||
private readonly _productService: IProductService
|
||||
private readonly _productService: IProductService,
|
||||
private readonly _extensionManagementService: IExtensionManagementService,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
@ -196,16 +204,16 @@ export class RemoteTerminalChannel extends Disposable implements IServerChannel<
|
|||
const workspaceFolders = args.workspaceFolders.map(reviveWorkspaceFolder);
|
||||
const activeWorkspaceFolder = args.activeWorkspaceFolder ? reviveWorkspaceFolder(args.activeWorkspaceFolder) : undefined;
|
||||
const activeFileResource = args.activeFileResource ? URI.revive(uriTransformer.transformIncoming(args.activeFileResource)) : undefined;
|
||||
const customVariableResolver = new CustomVariableResolver(baseEnv, workspaceFolders, activeFileResource, args.resolvedVariables);
|
||||
const customVariableResolver = new CustomVariableResolver(baseEnv, workspaceFolders, activeFileResource, args.resolvedVariables, this._extensionManagementService);
|
||||
const variableResolver = terminalEnvironment.createVariableResolver(activeWorkspaceFolder, process.env, customVariableResolver);
|
||||
|
||||
// Get the initial cwd
|
||||
const initialCwd = terminalEnvironment.getCwd(shellLaunchConfig, os.homedir(), variableResolver, activeWorkspaceFolder?.uri, args.configuration['terminal.integrated.cwd'], this._logService);
|
||||
const initialCwd = await terminalEnvironment.getCwd(shellLaunchConfig, os.homedir(), variableResolver, activeWorkspaceFolder?.uri, args.configuration['terminal.integrated.cwd'], this._logService);
|
||||
shellLaunchConfig.cwd = initialCwd;
|
||||
|
||||
const envPlatformKey = platform.isWindows ? 'terminal.integrated.env.windows' : (platform.isMacintosh ? 'terminal.integrated.env.osx' : 'terminal.integrated.env.linux');
|
||||
const envFromConfig = args.configuration[envPlatformKey];
|
||||
const env = terminalEnvironment.createTerminalEnvironment(
|
||||
const env = await terminalEnvironment.createTerminalEnvironment(
|
||||
shellLaunchConfig,
|
||||
envFromConfig,
|
||||
variableResolver,
|
||||
|
@ -222,7 +230,7 @@ export class RemoteTerminalChannel extends Disposable implements IServerChannel<
|
|||
}
|
||||
const envVariableCollections = new Map<string, IEnvironmentVariableCollection>(entries);
|
||||
const mergedCollection = new MergedEnvironmentVariableCollection(envVariableCollections);
|
||||
mergedCollection.applyToProcessEnvironment(env);
|
||||
await mergedCollection.applyToProcessEnvironment(env, variableResolver);
|
||||
}
|
||||
|
||||
// Fork the process and listen for messages
|
||||
|
|
|
@ -186,7 +186,7 @@ export async function setupServerServices(connectionToken: ServerConnectionToken
|
|||
const telemetryChannel = new ServerTelemetryChannel(accessor.get(IServerTelemetryService), appInsightsAppender);
|
||||
socketServer.registerChannel('telemetry', telemetryChannel);
|
||||
|
||||
socketServer.registerChannel(REMOTE_TERMINAL_CHANNEL_NAME, new RemoteTerminalChannel(environmentService, logService, ptyService, productService));
|
||||
socketServer.registerChannel(REMOTE_TERMINAL_CHANNEL_NAME, new RemoteTerminalChannel(environmentService, logService, ptyService, productService, extensionManagementService));
|
||||
|
||||
const remoteFileSystemChannel = new RemoteAgentFileSystemProviderChannel(logService, environmentService);
|
||||
socketServer.registerChannel(REMOTE_FILE_SYSTEM_CHANNEL_NAME, remoteFileSystemChannel);
|
||||
|
|
|
@ -57,6 +57,9 @@ export class MainThreadExtensionService implements MainThreadExtensionServiceSha
|
|||
public dispose(): void {
|
||||
}
|
||||
|
||||
$getExtension(extensionId: string) {
|
||||
return this._extensionService.getExtension(extensionId);
|
||||
}
|
||||
$activateExtension(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<void> {
|
||||
return this._internalExtensionService._activateById(extensionId, reason);
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ import { ExtHostEditorTabs, IExtHostEditorTabs } from 'vs/workbench/api/common/e
|
|||
import { ExtHostLoggerService } from 'vs/workbench/api/common/extHostLoggerService';
|
||||
import { ILoggerService, ILogService } from 'vs/platform/log/common/log';
|
||||
import { ExtHostLogService } from 'vs/workbench/api/common/extHostLogService';
|
||||
import { ExtHostVariableResolverProviderService, IExtHostVariableResolverProvider } from 'vs/workbench/api/common/extHostVariableResolverService';
|
||||
|
||||
registerSingleton(ILoggerService, ExtHostLoggerService);
|
||||
registerSingleton(ILogService, ExtHostLogService);
|
||||
|
@ -48,3 +49,4 @@ registerSingleton(IExtHostWorkspace, ExtHostWorkspace);
|
|||
registerSingleton(IExtHostSecretState, ExtHostSecretState);
|
||||
registerSingleton(IExtHostTelemetry, ExtHostTelemetry);
|
||||
registerSingleton(IExtHostEditorTabs, ExtHostEditorTabs);
|
||||
registerSingleton(IExtHostVariableResolverProvider, ExtHostVariableResolverProviderService);
|
||||
|
|
|
@ -1107,6 +1107,7 @@ export interface MainThreadTaskShape extends IDisposable {
|
|||
}
|
||||
|
||||
export interface MainThreadExtensionServiceShape extends IDisposable {
|
||||
$getExtension(extensionId: string): Promise<Dto<IExtensionDescription> | undefined>;
|
||||
$activateExtension(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<void>;
|
||||
$onWillActivateExtension(extensionId: ExtensionIdentifier): Promise<void>;
|
||||
$onDidActivateExtension(extensionId: ExtensionIdentifier, codeLoadingTime: number, activateCallTime: number, activateResolvedTime: number, activationReason: ExtensionActivationReason): void;
|
||||
|
|
|
@ -3,36 +3,29 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as path from 'vs/base/common/path';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { asPromise } from 'vs/base/common/async';
|
||||
import {
|
||||
MainContext, MainThreadDebugServiceShape, ExtHostDebugServiceShape, DebugSessionUUID,
|
||||
IBreakpointsDeltaDto, ISourceMultiBreakpointDto, IFunctionBreakpointDto, IDebugSessionDto
|
||||
} from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { Disposable, Position, Location, SourceBreakpoint, FunctionBreakpoint, DebugAdapterServer, DebugAdapterExecutable, DataBreakpoint, DebugConsoleMode, DebugAdapterInlineImplementation, DebugAdapterNamedPipeServer, TextDiffTabInput, NotebookDiffEditorTabInput, TextTabInput, NotebookEditorTabInput, CustomEditorTabInput } from 'vs/workbench/api/common/extHostTypes';
|
||||
import { AbstractDebugAdapter } from 'vs/workbench/contrib/debug/common/abstractDebugAdapter';
|
||||
import { IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
|
||||
import { IExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensionService';
|
||||
import { ExtHostDocumentsAndEditors, IExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
|
||||
import { IDebuggerContribution, IConfig, IDebugAdapter, IDebugAdapterServer, IDebugAdapterExecutable, IAdapterDescriptor, IDebugAdapterNamedPipeServer } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
|
||||
import { AbstractVariableResolverService } from 'vs/workbench/services/configurationResolver/common/variableResolver';
|
||||
import { ExtHostConfigProvider, IExtHostConfiguration } from '../common/extHostConfiguration';
|
||||
import { convertToVSCPaths, convertToDAPaths, isDebuggerMainContribution } from 'vs/workbench/contrib/debug/common/debugUtils';
|
||||
import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry';
|
||||
import { ISignService } from 'vs/platform/sign/common/sign';
|
||||
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
|
||||
import type * as vscode from 'vscode';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { withNullAsUndefined } from 'vs/base/common/types';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { withNullAsUndefined } from 'vs/base/common/types';
|
||||
import * as process from 'vs/base/common/process';
|
||||
import { ISignService } from 'vs/platform/sign/common/sign';
|
||||
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
|
||||
import { DebugSessionUUID, ExtHostDebugServiceShape, IBreakpointsDeltaDto, IDebugSessionDto, IFunctionBreakpointDto, ISourceMultiBreakpointDto, MainContext, MainThreadDebugServiceShape } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { IExtHostEditorTabs } from 'vs/workbench/api/common/extHostEditorTabs';
|
||||
import { IExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensionService';
|
||||
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
|
||||
import { DataBreakpoint, DebugAdapterExecutable, DebugAdapterInlineImplementation, DebugAdapterNamedPipeServer, DebugAdapterServer, DebugConsoleMode, Disposable, FunctionBreakpoint, Location, Position, SourceBreakpoint } from 'vs/workbench/api/common/extHostTypes';
|
||||
import { IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
|
||||
import { AbstractDebugAdapter } from 'vs/workbench/contrib/debug/common/abstractDebugAdapter';
|
||||
import { IAdapterDescriptor, IConfig, IDebugAdapter, IDebugAdapterExecutable, IDebugAdapterNamedPipeServer, IDebugAdapterServer, IDebuggerContribution } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { convertToDAPaths, convertToVSCPaths, isDebuggerMainContribution } from 'vs/workbench/contrib/debug/common/debugUtils';
|
||||
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry';
|
||||
import { Dto } from 'vs/workbench/services/extensions/common/proxyIdentifier';
|
||||
import type * as vscode from 'vscode';
|
||||
import { IExtHostConfiguration } from '../common/extHostConfiguration';
|
||||
import { IExtHostVariableResolverProvider } from './extHostVariableResolverService';
|
||||
|
||||
export const IExtHostDebugService = createDecorator<IExtHostDebugService>('IExtHostDebugService');
|
||||
|
||||
|
@ -101,17 +94,15 @@ export abstract class ExtHostDebugServiceBase implements IExtHostDebugService, E
|
|||
private _debugAdapters: Map<number, IDebugAdapter>;
|
||||
private _debugAdaptersTrackers: Map<number, vscode.DebugAdapterTracker>;
|
||||
|
||||
private _variableResolver: IConfigurationResolverService | undefined;
|
||||
|
||||
private _signService: ISignService | undefined;
|
||||
|
||||
constructor(
|
||||
@IExtHostRpcService extHostRpcService: IExtHostRpcService,
|
||||
@IExtHostWorkspace protected _workspaceService: IExtHostWorkspace,
|
||||
@IExtHostExtensionService private _extensionService: IExtHostExtensionService,
|
||||
@IExtHostDocumentsAndEditors private _editorsService: IExtHostDocumentsAndEditors,
|
||||
@IExtHostConfiguration protected _configurationService: IExtHostConfiguration,
|
||||
@IExtHostEditorTabs protected _editorTabs: IExtHostEditorTabs
|
||||
@IExtHostEditorTabs protected _editorTabs: IExtHostEditorTabs,
|
||||
@IExtHostVariableResolverProvider private _variableResolver: IExtHostVariableResolverProvider,
|
||||
) {
|
||||
this._configProviderHandleCounter = 0;
|
||||
this._configProviders = [];
|
||||
|
@ -371,13 +362,7 @@ export abstract class ExtHostDebugServiceBase implements IExtHostDebugService, E
|
|||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
protected abstract createVariableResolver(folders: vscode.WorkspaceFolder[], editorService: ExtHostDocumentsAndEditors, configurationService: ExtHostConfigProvider): AbstractVariableResolverService;
|
||||
|
||||
public async $substituteVariables(folderUri: UriComponents | undefined, config: IConfig): Promise<IConfig> {
|
||||
if (!this._variableResolver) {
|
||||
const [workspaceFolders, configProvider] = await Promise.all([this._workspaceService.getWorkspaceFolders2(), this._configurationService.getConfigProvider()]);
|
||||
this._variableResolver = this.createVariableResolver(workspaceFolders || [], this._editorsService, configProvider!);
|
||||
}
|
||||
let ws: IWorkspaceFolder | undefined;
|
||||
const folder = await this.getFolder(folderUri);
|
||||
if (folder) {
|
||||
|
@ -390,7 +375,8 @@ export abstract class ExtHostDebugServiceBase implements IExtHostDebugService, E
|
|||
}
|
||||
};
|
||||
}
|
||||
return this._variableResolver.resolveAnyAsync(ws, config);
|
||||
const variableResolver = await this._variableResolver.getResolver();
|
||||
return variableResolver.resolveAnyAsync(ws, config);
|
||||
}
|
||||
|
||||
protected createDebugAdapter(adapter: IAdapterDescriptor, session: ExtHostDebugSession): AbstractDebugAdapter | undefined {
|
||||
|
@ -939,94 +925,6 @@ export class ExtHostDebugConsole {
|
|||
}
|
||||
}
|
||||
|
||||
export class ExtHostVariableResolverService extends AbstractVariableResolverService {
|
||||
|
||||
constructor(folders: vscode.WorkspaceFolder[],
|
||||
editorService: ExtHostDocumentsAndEditors | undefined,
|
||||
configurationService: ExtHostConfigProvider,
|
||||
editorTabs: IExtHostEditorTabs,
|
||||
workspaceService?: IExtHostWorkspace,
|
||||
userHome?: string) {
|
||||
function getActiveUri(): URI | undefined {
|
||||
if (editorService) {
|
||||
const activeEditor = editorService.activeEditor();
|
||||
if (activeEditor) {
|
||||
return activeEditor.document.uri;
|
||||
}
|
||||
const activeTab = editorTabs.tabGroups.all.find(group => group.isActive)?.activeTab;
|
||||
if (activeTab !== undefined) {
|
||||
// Resolve a resource from the tab
|
||||
if (activeTab.kind instanceof TextDiffTabInput || activeTab.kind instanceof NotebookDiffEditorTabInput) {
|
||||
return activeTab.kind.modified;
|
||||
} else if (activeTab.kind instanceof TextTabInput || activeTab.kind instanceof NotebookEditorTabInput || activeTab.kind instanceof CustomEditorTabInput) {
|
||||
return activeTab.kind.uri;
|
||||
}
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
super({
|
||||
getFolderUri: (folderName: string): URI | undefined => {
|
||||
const found = folders.filter(f => f.name === folderName);
|
||||
if (found && found.length > 0) {
|
||||
return found[0].uri;
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
getWorkspaceFolderCount: (): number => {
|
||||
return folders.length;
|
||||
},
|
||||
getConfigurationValue: (folderUri: URI | undefined, section: string): string | undefined => {
|
||||
return configurationService.getConfiguration(undefined, folderUri).get<string>(section);
|
||||
},
|
||||
getAppRoot: (): string | undefined => {
|
||||
return process.cwd();
|
||||
},
|
||||
getExecPath: (): string | undefined => {
|
||||
return process.env['VSCODE_EXEC_PATH'];
|
||||
},
|
||||
getFilePath: (): string | undefined => {
|
||||
const activeUri = getActiveUri();
|
||||
if (activeUri) {
|
||||
return path.normalize(activeUri.fsPath);
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
getWorkspaceFolderPathForFile: (): string | undefined => {
|
||||
if (workspaceService) {
|
||||
const activeUri = getActiveUri();
|
||||
if (activeUri) {
|
||||
const ws = workspaceService.getWorkspaceFolder(activeUri);
|
||||
if (ws) {
|
||||
return path.normalize(ws.uri.fsPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
getSelectedText: (): string | undefined => {
|
||||
if (editorService) {
|
||||
const activeEditor = editorService.activeEditor();
|
||||
if (activeEditor && !activeEditor.selection.isEmpty) {
|
||||
return activeEditor.document.getText(activeEditor.selection);
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
getLineNumber: (): string | undefined => {
|
||||
if (editorService) {
|
||||
const activeEditor = editorService.activeEditor();
|
||||
if (activeEditor) {
|
||||
return String(activeEditor.selection.end.line + 1);
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}, undefined, userHome ? Promise.resolve(userHome) : undefined, Promise.resolve(process.env));
|
||||
}
|
||||
}
|
||||
|
||||
interface ConfigProviderTuple {
|
||||
type: string;
|
||||
handle: number;
|
||||
|
@ -1108,14 +1006,10 @@ export class WorkerExtHostDebugService extends ExtHostDebugServiceBase {
|
|||
@IExtHostRpcService extHostRpcService: IExtHostRpcService,
|
||||
@IExtHostWorkspace workspaceService: IExtHostWorkspace,
|
||||
@IExtHostExtensionService extensionService: IExtHostExtensionService,
|
||||
@IExtHostDocumentsAndEditors editorsService: IExtHostDocumentsAndEditors,
|
||||
@IExtHostConfiguration configurationService: IExtHostConfiguration,
|
||||
@IExtHostEditorTabs editorTabs: IExtHostEditorTabs
|
||||
@IExtHostEditorTabs editorTabs: IExtHostEditorTabs,
|
||||
@IExtHostVariableResolverProvider variableResolver: IExtHostVariableResolverProvider
|
||||
) {
|
||||
super(extHostRpcService, workspaceService, extensionService, editorsService, configurationService, editorTabs);
|
||||
}
|
||||
|
||||
protected createVariableResolver(folders: vscode.WorkspaceFolder[], editorService: ExtHostDocumentsAndEditors, configurationService: ExtHostConfigProvider): AbstractVariableResolverService {
|
||||
return new ExtHostVariableResolverService(folders, editorService, configurationService, this._editorTabs);
|
||||
super(extHostRpcService, workspaceService, extensionService, configurationService, editorTabs, variableResolver);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -229,6 +229,15 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
|
|||
return false;
|
||||
}
|
||||
|
||||
public async getExtension(extensionId: string): Promise<IExtensionDescription | undefined> {
|
||||
const ext = await this._mainThreadExtensionsProxy.$getExtension(extensionId);
|
||||
return ext && {
|
||||
...ext,
|
||||
identifier: new ExtensionIdentifier(ext.identifier.value),
|
||||
extensionLocation: URI.revive(ext.extensionLocation),
|
||||
};
|
||||
}
|
||||
|
||||
private _activateByEvent(activationEvent: string, startup: boolean): Promise<void> {
|
||||
return this._activator.activateByEvent(activationEvent, startup);
|
||||
}
|
||||
|
@ -885,6 +894,7 @@ export const IExtHostExtensionService = createDecorator<IExtHostExtensionService
|
|||
export interface IExtHostExtensionService extends AbstractExtHostExtensionService {
|
||||
readonly _serviceBrand: undefined;
|
||||
initialize(): Promise<void>;
|
||||
getExtension(extensionId: string): Promise<IExtensionDescription | undefined>;
|
||||
isActivated(extensionId: ExtensionIdentifier): boolean;
|
||||
activateByIdWithErrors(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<void>;
|
||||
deactivateAll(): Promise<void>;
|
||||
|
|
167
src/vs/workbench/api/common/extHostVariableResolverService.ts
Normal file
167
src/vs/workbench/api/common/extHostVariableResolverService.ts
Normal file
|
@ -0,0 +1,167 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Lazy } from 'vs/base/common/lazy';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import * as path from 'vs/base/common/path';
|
||||
import * as process from 'vs/base/common/process';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
|
||||
import { IExtHostEditorTabs } from 'vs/workbench/api/common/extHostEditorTabs';
|
||||
import { IExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensionService';
|
||||
import { CustomEditorTabInput, NotebookDiffEditorTabInput, NotebookEditorTabInput, TextDiffTabInput, TextTabInput } from 'vs/workbench/api/common/extHostTypes';
|
||||
import { IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
|
||||
import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver';
|
||||
import { AbstractVariableResolverService } from 'vs/workbench/services/configurationResolver/common/variableResolver';
|
||||
import * as vscode from 'vscode';
|
||||
import { ExtHostConfigProvider, IExtHostConfiguration } from './extHostConfiguration';
|
||||
|
||||
export interface IExtHostVariableResolverProvider {
|
||||
readonly _serviceBrand: undefined;
|
||||
getResolver(): Promise<IConfigurationResolverService>;
|
||||
}
|
||||
|
||||
export const IExtHostVariableResolverProvider = createDecorator<IExtHostVariableResolverProvider>('IExtHostVariableResolverProvider');
|
||||
|
||||
interface DynamicContext {
|
||||
folders: vscode.WorkspaceFolder[];
|
||||
}
|
||||
|
||||
class ExtHostVariableResolverService extends AbstractVariableResolverService {
|
||||
|
||||
constructor(
|
||||
extensionService: IExtHostExtensionService,
|
||||
workspaceService: IExtHostWorkspace,
|
||||
editorService: IExtHostDocumentsAndEditors,
|
||||
editorTabs: IExtHostEditorTabs,
|
||||
configProvider: ExtHostConfigProvider,
|
||||
context: DynamicContext,
|
||||
homeDir: string | undefined,
|
||||
) {
|
||||
function getActiveUri(): URI | undefined {
|
||||
if (editorService) {
|
||||
const activeEditor = editorService.activeEditor();
|
||||
if (activeEditor) {
|
||||
return activeEditor.document.uri;
|
||||
}
|
||||
const activeTab = editorTabs.tabGroups.all.find(group => group.isActive)?.activeTab;
|
||||
if (activeTab !== undefined) {
|
||||
// Resolve a resource from the tab
|
||||
if (activeTab.kind instanceof TextDiffTabInput || activeTab.kind instanceof NotebookDiffEditorTabInput) {
|
||||
return activeTab.kind.modified;
|
||||
} else if (activeTab.kind instanceof TextTabInput || activeTab.kind instanceof NotebookEditorTabInput || activeTab.kind instanceof CustomEditorTabInput) {
|
||||
return activeTab.kind.uri;
|
||||
}
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
super({
|
||||
getFolderUri: (folderName: string): URI | undefined => {
|
||||
const found = context.folders.filter(f => f.name === folderName);
|
||||
if (found && found.length > 0) {
|
||||
return found[0].uri;
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
getWorkspaceFolderCount: (): number => {
|
||||
return context.folders.length;
|
||||
},
|
||||
getConfigurationValue: (folderUri: URI | undefined, section: string): string | undefined => {
|
||||
return configProvider.getConfiguration(undefined, folderUri).get<string>(section);
|
||||
},
|
||||
getAppRoot: (): string | undefined => {
|
||||
return process.cwd();
|
||||
},
|
||||
getExecPath: (): string | undefined => {
|
||||
return process.env['VSCODE_EXEC_PATH'];
|
||||
},
|
||||
getFilePath: (): string | undefined => {
|
||||
const activeUri = getActiveUri();
|
||||
if (activeUri) {
|
||||
return path.normalize(activeUri.fsPath);
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
getWorkspaceFolderPathForFile: (): string | undefined => {
|
||||
if (workspaceService) {
|
||||
const activeUri = getActiveUri();
|
||||
if (activeUri) {
|
||||
const ws = workspaceService.getWorkspaceFolder(activeUri);
|
||||
if (ws) {
|
||||
return path.normalize(ws.uri.fsPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
getSelectedText: (): string | undefined => {
|
||||
if (editorService) {
|
||||
const activeEditor = editorService.activeEditor();
|
||||
if (activeEditor && !activeEditor.selection.isEmpty) {
|
||||
return activeEditor.document.getText(activeEditor.selection);
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
getLineNumber: (): string | undefined => {
|
||||
if (editorService) {
|
||||
const activeEditor = editorService.activeEditor();
|
||||
if (activeEditor) {
|
||||
return String(activeEditor.selection.end.line + 1);
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
getExtension: (id) => {
|
||||
return extensionService.getExtension(id);
|
||||
},
|
||||
}, undefined, homeDir ? Promise.resolve(homeDir) : undefined, Promise.resolve(process.env));
|
||||
}
|
||||
}
|
||||
|
||||
export class ExtHostVariableResolverProviderService extends Disposable implements IExtHostVariableResolverProvider {
|
||||
declare readonly _serviceBrand: undefined;
|
||||
|
||||
private _resolver = new Lazy(async () => {
|
||||
const configProvider = await this.configurationService.getConfigProvider();
|
||||
const folders = await this.workspaceService.getWorkspaceFolders2() || [];
|
||||
|
||||
const dynamic: DynamicContext = { folders };
|
||||
this._register(this.workspaceService.onDidChangeWorkspace(async e => {
|
||||
dynamic.folders = await this.workspaceService.getWorkspaceFolders2() || [];
|
||||
}));
|
||||
|
||||
return new ExtHostVariableResolverService(
|
||||
this.extensionService,
|
||||
this.workspaceService,
|
||||
this.editorService,
|
||||
this.editorTabs,
|
||||
configProvider,
|
||||
dynamic,
|
||||
this.homeDir(),
|
||||
);
|
||||
});
|
||||
|
||||
constructor(
|
||||
@IExtHostExtensionService private readonly extensionService: IExtHostExtensionService,
|
||||
@IExtHostWorkspace private readonly workspaceService: IExtHostWorkspace,
|
||||
@IExtHostDocumentsAndEditors private readonly editorService: IExtHostDocumentsAndEditors,
|
||||
@IExtHostConfiguration private readonly configurationService: IExtHostConfiguration,
|
||||
@IExtHostEditorTabs private readonly editorTabs: IExtHostEditorTabs,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
public getResolver(): Promise<IConfigurationResolverService> {
|
||||
return this._resolver.getValue();
|
||||
}
|
||||
|
||||
protected homeDir(): string | undefined {
|
||||
return undefined;
|
||||
}
|
||||
}
|
|
@ -20,6 +20,8 @@ import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePa
|
|||
import { ExtensionStoragePaths } from 'vs/workbench/api/node/extHostStoragePaths';
|
||||
import { ExtHostLoggerService } from 'vs/workbench/api/node/extHostLoggerService';
|
||||
import { ILoggerService } from 'vs/platform/log/common/log';
|
||||
import { NodeExtHostVariableResolverProviderService } from 'vs/workbench/api/node/extHostVariableResolverService';
|
||||
import { IExtHostVariableResolverProvider } from 'vs/workbench/api/common/extHostVariableResolverService';
|
||||
|
||||
// #########################################################################
|
||||
// ### ###
|
||||
|
@ -36,3 +38,4 @@ registerSingleton(IExtHostSearch, NativeExtHostSearch);
|
|||
registerSingleton(IExtHostTask, ExtHostTask);
|
||||
registerSingleton(IExtHostTerminalService, ExtHostTerminalService);
|
||||
registerSingleton(IExtHostTunnelService, ExtHostTunnelService);
|
||||
registerSingleton(IExtHostVariableResolverProvider, NodeExtHostVariableResolverProviderService);
|
||||
|
|
|
@ -3,31 +3,29 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import type * as vscode from 'vscode';
|
||||
import { homedir } from 'os';
|
||||
import { createCancelablePromise, firstParallel } from 'vs/base/common/async';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
import { DebugAdapterExecutable, ThemeIcon } from 'vs/workbench/api/common/extHostTypes';
|
||||
import { ExecutableDebugAdapter, SocketDebugAdapter, NamedPipeDebugAdapter } from 'vs/workbench/contrib/debug/node/debugAdapter';
|
||||
import { AbstractDebugAdapter } from 'vs/workbench/contrib/debug/common/abstractDebugAdapter';
|
||||
import { IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
|
||||
import { IExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensionService';
|
||||
import { IExtHostDocumentsAndEditors, ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
|
||||
import { IAdapterDescriptor } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { IExtHostConfiguration, ExtHostConfigProvider } from '../common/extHostConfiguration';
|
||||
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry';
|
||||
import { IExtHostTerminalService } from 'vs/workbench/api/common/extHostTerminalService';
|
||||
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
|
||||
import { ExtHostDebugServiceBase, ExtHostDebugSession, ExtHostVariableResolverService } from 'vs/workbench/api/common/extHostDebugService';
|
||||
import * as nls from 'vs/nls';
|
||||
import { IExternalTerminalService } from 'vs/platform/externalTerminal/common/externalTerminal';
|
||||
import { LinuxExternalTerminalService, MacExternalTerminalService, WindowsExternalTerminalService } from 'vs/platform/externalTerminal/node/externalTerminalService';
|
||||
import { ISignService } from 'vs/platform/sign/common/sign';
|
||||
import { SignService } from 'vs/platform/sign/node/signService';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { AbstractVariableResolverService } from 'vs/workbench/services/configurationResolver/common/variableResolver';
|
||||
import { createCancelablePromise, firstParallel } from 'vs/base/common/async';
|
||||
import { hasChildProcesses, prepareCommand } from 'vs/workbench/contrib/debug/node/terminals';
|
||||
import { ExtHostDebugServiceBase, ExtHostDebugSession } from 'vs/workbench/api/common/extHostDebugService';
|
||||
import { IExtHostEditorTabs } from 'vs/workbench/api/common/extHostEditorTabs';
|
||||
import { IExternalTerminalService } from 'vs/platform/externalTerminal/common/externalTerminal';
|
||||
import { WindowsExternalTerminalService, MacExternalTerminalService, LinuxExternalTerminalService } from 'vs/platform/externalTerminal/node/externalTerminalService';
|
||||
import { IExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensionService';
|
||||
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
|
||||
import { IExtHostTerminalService } from 'vs/workbench/api/common/extHostTerminalService';
|
||||
import { DebugAdapterExecutable, ThemeIcon } from 'vs/workbench/api/common/extHostTypes';
|
||||
import { IExtHostVariableResolverProvider } from 'vs/workbench/api/common/extHostVariableResolverService';
|
||||
import { IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
|
||||
import { AbstractDebugAdapter } from 'vs/workbench/contrib/debug/common/abstractDebugAdapter';
|
||||
import { IAdapterDescriptor } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { ExecutableDebugAdapter, NamedPipeDebugAdapter, SocketDebugAdapter } from 'vs/workbench/contrib/debug/node/debugAdapter';
|
||||
import { hasChildProcesses, prepareCommand } from 'vs/workbench/contrib/debug/node/terminals';
|
||||
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry';
|
||||
import type * as vscode from 'vscode';
|
||||
import { ExtHostConfigProvider, IExtHostConfiguration } from '../common/extHostConfiguration';
|
||||
|
||||
export class ExtHostDebugService extends ExtHostDebugServiceBase {
|
||||
|
||||
|
@ -40,12 +38,12 @@ export class ExtHostDebugService extends ExtHostDebugServiceBase {
|
|||
@IExtHostRpcService extHostRpcService: IExtHostRpcService,
|
||||
@IExtHostWorkspace workspaceService: IExtHostWorkspace,
|
||||
@IExtHostExtensionService extensionService: IExtHostExtensionService,
|
||||
@IExtHostDocumentsAndEditors editorsService: IExtHostDocumentsAndEditors,
|
||||
@IExtHostConfiguration configurationService: IExtHostConfiguration,
|
||||
@IExtHostTerminalService private _terminalService: IExtHostTerminalService,
|
||||
@IExtHostEditorTabs editorTabs: IExtHostEditorTabs
|
||||
@IExtHostEditorTabs editorTabs: IExtHostEditorTabs,
|
||||
@IExtHostVariableResolverProvider variableResolver: IExtHostVariableResolverProvider,
|
||||
) {
|
||||
super(extHostRpcService, workspaceService, extensionService, editorsService, configurationService, editorTabs);
|
||||
super(extHostRpcService, workspaceService, extensionService, configurationService, editorTabs, variableResolver);
|
||||
}
|
||||
|
||||
protected override createDebugAdapter(adapter: IAdapterDescriptor, session: ExtHostDebugSession): AbstractDebugAdapter | undefined {
|
||||
|
@ -154,10 +152,6 @@ export class ExtHostDebugService extends ExtHostDebugServiceBase {
|
|||
}
|
||||
return super.$runInTerminal(args, sessionId);
|
||||
}
|
||||
|
||||
protected createVariableResolver(folders: vscode.WorkspaceFolder[], editorService: ExtHostDocumentsAndEditors, configurationService: ExtHostConfigProvider): AbstractVariableResolverService {
|
||||
return new ExtHostVariableResolverService(folders, editorService, configurationService, this._editorTabs, this._workspaceService, homedir());
|
||||
}
|
||||
}
|
||||
|
||||
let externalTerminalService: IExternalTerminalService | undefined = undefined;
|
||||
|
|
|
@ -11,7 +11,6 @@ import * as types from 'vs/workbench/api/common/extHostTypes';
|
|||
import { IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
|
||||
import type * as vscode from 'vscode';
|
||||
import * as tasks from '../common/shared/tasks';
|
||||
import { ExtHostVariableResolverService } from 'vs/workbench/api/common/extHostDebugService';
|
||||
import { IExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
|
||||
import { IExtHostConfiguration } from 'vs/workbench/api/common/extHostConfiguration';
|
||||
import { IWorkspaceFolder, WorkspaceFolder } from 'vs/platform/workspace/common/workspace';
|
||||
|
@ -23,13 +22,11 @@ import { ExtHostTaskBase, TaskHandleDTO, TaskDTO, CustomExecutionDTO, HandlerDat
|
|||
import { Schemas } from 'vs/base/common/network';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IExtHostApiDeprecationService } from 'vs/workbench/api/common/extHostApiDeprecationService';
|
||||
import { IExtHostEditorTabs } from 'vs/workbench/api/common/extHostEditorTabs';
|
||||
import * as resources from 'vs/base/common/resources';
|
||||
import { homedir } from 'os';
|
||||
import { IExtHostVariableResolverProvider } from 'vs/workbench/api/common/extHostVariableResolverService';
|
||||
|
||||
export class ExtHostTask extends ExtHostTaskBase {
|
||||
private _variableResolver: ExtHostVariableResolverService | undefined;
|
||||
|
||||
constructor(
|
||||
@IExtHostRpcService extHostRpc: IExtHostRpcService,
|
||||
@IExtHostInitDataService initData: IExtHostInitDataService,
|
||||
|
@ -39,7 +36,7 @@ export class ExtHostTask extends ExtHostTaskBase {
|
|||
@IExtHostTerminalService extHostTerminalService: IExtHostTerminalService,
|
||||
@ILogService logService: ILogService,
|
||||
@IExtHostApiDeprecationService deprecationService: IExtHostApiDeprecationService,
|
||||
@IExtHostEditorTabs private readonly editorTabs: IExtHostEditorTabs,
|
||||
@IExtHostVariableResolverProvider private readonly variableResolver: IExtHostVariableResolverProvider,
|
||||
) {
|
||||
super(extHostRpc, initData, workspaceService, editorService, configurationService, extHostTerminalService, logService, deprecationService);
|
||||
if (initData.remote.isRemote && initData.remote.authority) {
|
||||
|
@ -128,14 +125,6 @@ export class ExtHostTask extends ExtHostTaskBase {
|
|||
return resolvedTaskDTO;
|
||||
}
|
||||
|
||||
private async getVariableResolver(workspaceFolders: vscode.WorkspaceFolder[]): Promise<ExtHostVariableResolverService> {
|
||||
if (this._variableResolver === undefined) {
|
||||
const configProvider = await this._configurationService.getConfigProvider();
|
||||
this._variableResolver = new ExtHostVariableResolverService(workspaceFolders, this._editorService, configProvider, this.editorTabs, this.workspaceService, homedir());
|
||||
}
|
||||
return this._variableResolver;
|
||||
}
|
||||
|
||||
private async getAFolder(workspaceFolders: vscode.WorkspaceFolder[] | undefined): Promise<IWorkspaceFolder> {
|
||||
let folder = (workspaceFolders && workspaceFolders.length > 0) ? workspaceFolders[0] : undefined;
|
||||
if (!folder) {
|
||||
|
@ -161,7 +150,7 @@ export class ExtHostTask extends ExtHostTaskBase {
|
|||
const workspaceFolder = await this._workspaceProvider.resolveWorkspaceFolder(uri);
|
||||
const workspaceFolders = (await this._workspaceProvider.getWorkspaceFolders2()) ?? [];
|
||||
|
||||
const resolver = await this.getVariableResolver(workspaceFolders);
|
||||
const resolver = await this.variableResolver.getResolver();
|
||||
const ws: IWorkspaceFolder = workspaceFolder ? {
|
||||
uri: workspaceFolder.uri,
|
||||
name: workspaceFolder.name,
|
||||
|
|
13
src/vs/workbench/api/node/extHostVariableResolverService.ts
Normal file
13
src/vs/workbench/api/node/extHostVariableResolverService.ts
Normal file
|
@ -0,0 +1,13 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { homedir } from 'os';
|
||||
import { ExtHostVariableResolverProviderService } from 'vs/workbench/api/common/extHostVariableResolverService';
|
||||
|
||||
export class NodeExtHostVariableResolverProviderService extends ExtHostVariableResolverProviderService {
|
||||
protected override homeDir(): string | undefined {
|
||||
return homedir();
|
||||
}
|
||||
}
|
|
@ -388,7 +388,7 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce
|
|||
baseEnv = await this._terminalProfileResolverService.getEnvironment(this.remoteAuthority);
|
||||
}
|
||||
|
||||
const env = terminalEnvironment.createTerminalEnvironment(shellLaunchConfig, envFromConfigValue, variableResolver, this._productService.version, this._configHelper.config.detectLocale, baseEnv);
|
||||
const env = await terminalEnvironment.createTerminalEnvironment(shellLaunchConfig, envFromConfigValue, variableResolver, this._productService.version, this._configHelper.config.detectLocale, baseEnv);
|
||||
if (!this._isDisposed && !shellLaunchConfig.strictEnv && !shellLaunchConfig.hideFromUser) {
|
||||
this._extEnvironmentVariableCollection = this._environmentVariableService.mergedCollection;
|
||||
this._register(this._environmentVariableService.onDidChangeCollections(newCollection => this._onEnvironmentVariableCollectionChange(newCollection)));
|
||||
|
@ -398,7 +398,7 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce
|
|||
// info widget. While technically these could differ due to the slight change of a race
|
||||
// condition, the chance is minimal plus the impact on the user is also not that great
|
||||
// if it happens - it's not worth adding plumbing to sync back the resolved collection.
|
||||
this._extEnvironmentVariableCollection.applyToProcessEnvironment(env, variableResolver);
|
||||
await this._extEnvironmentVariableCollection.applyToProcessEnvironment(env, variableResolver);
|
||||
if (this._extEnvironmentVariableCollection.map.size > 0) {
|
||||
this.environmentVariableInfo = new EnvironmentVariableInfoChangesActive(this._extEnvironmentVariableCollection);
|
||||
this._onEnvironmentVariableInfoChange.fire(this.environmentVariableInfo);
|
||||
|
@ -423,7 +423,7 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce
|
|||
|
||||
const activeWorkspaceRootUri = this._historyService.getLastActiveWorkspaceRoot(Schemas.file);
|
||||
|
||||
const initialCwd = terminalEnvironment.getCwd(
|
||||
const initialCwd = await terminalEnvironment.getCwd(
|
||||
shellLaunchConfig,
|
||||
userHome,
|
||||
variableResolver,
|
||||
|
|
|
@ -355,25 +355,23 @@ export abstract class BaseTerminalProfileResolverService implements ITerminalPro
|
|||
const env = await this._context.getEnvironment(options.remoteAuthority);
|
||||
const activeWorkspaceRootUri = this._historyService.getLastActiveWorkspaceRoot(options.remoteAuthority ? Schemas.vscodeRemote : Schemas.file);
|
||||
const lastActiveWorkspace = activeWorkspaceRootUri ? withNullAsUndefined(this._workspaceContextService.getWorkspaceFolder(activeWorkspaceRootUri)) : undefined;
|
||||
profile.path = this._resolveVariables(profile.path, env, lastActiveWorkspace);
|
||||
profile.path = await this._resolveVariables(profile.path, env, lastActiveWorkspace);
|
||||
|
||||
// Resolve args variables
|
||||
if (profile.args) {
|
||||
if (typeof profile.args === 'string') {
|
||||
profile.args = this._resolveVariables(profile.args, env, lastActiveWorkspace);
|
||||
profile.args = await this._resolveVariables(profile.args, env, lastActiveWorkspace);
|
||||
} else {
|
||||
for (let i = 0; i < profile.args.length; i++) {
|
||||
profile.args[i] = this._resolveVariables(profile.args[i], env, lastActiveWorkspace);
|
||||
}
|
||||
profile.args = await Promise.all(profile.args.map(arg => this._resolveVariables(arg, env, lastActiveWorkspace)));
|
||||
}
|
||||
}
|
||||
|
||||
return profile;
|
||||
}
|
||||
|
||||
private _resolveVariables(value: string, env: IProcessEnvironment, lastActiveWorkspace: IWorkspaceFolder | undefined) {
|
||||
private async _resolveVariables(value: string, env: IProcessEnvironment, lastActiveWorkspace: IWorkspaceFolder | undefined) {
|
||||
try {
|
||||
value = this._configurationResolverService.resolveWithEnvironment(env, lastActiveWorkspace, value);
|
||||
value = await this._configurationResolverService.resolveWithEnvironment(env, lastActiveWorkspace, value);
|
||||
} catch (e) {
|
||||
this._logService.error(`Could not resolve shell`, e);
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'
|
|||
import { Event } from 'vs/base/common/event';
|
||||
import { IProcessEnvironment } from 'vs/base/common/platform';
|
||||
import { ThemeIcon } from 'vs/platform/theme/common/themeService';
|
||||
import { VariableResolver } from 'vs/workbench/contrib/terminal/common/terminalEnvironment';
|
||||
|
||||
export const IEnvironmentVariableService = createDecorator<IEnvironmentVariableService>('environmentVariableService');
|
||||
|
||||
|
@ -51,7 +52,7 @@ export interface IMergedEnvironmentVariableCollection {
|
|||
* @param variableResolver An optional function to use to resolve variables within the
|
||||
* environment values.
|
||||
*/
|
||||
applyToProcessEnvironment(env: IProcessEnvironment, variableResolver?: (str: string) => string): void;
|
||||
applyToProcessEnvironment(env: IProcessEnvironment, variableResolver?: VariableResolver): Promise<void>;
|
||||
|
||||
/**
|
||||
* Generates a diff of this connection against another. Returns undefined if the collections are
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
import { IProcessEnvironment, isWindows } from 'vs/base/common/platform';
|
||||
import { EnvironmentVariableMutatorType, IEnvironmentVariableCollection, IExtensionOwnedEnvironmentVariableMutator, IMergedEnvironmentVariableCollection, IMergedEnvironmentVariableCollectionDiff } from 'vs/workbench/contrib/terminal/common/environmentVariable';
|
||||
import { VariableResolver } from 'vs/workbench/contrib/terminal/common/terminalEnvironment';
|
||||
|
||||
export class MergedEnvironmentVariableCollection implements IMergedEnvironmentVariableCollection {
|
||||
readonly map: Map<string, IExtensionOwnedEnvironmentVariableMutator[]> = new Map();
|
||||
|
@ -41,16 +42,16 @@ export class MergedEnvironmentVariableCollection implements IMergedEnvironmentVa
|
|||
});
|
||||
}
|
||||
|
||||
applyToProcessEnvironment(env: IProcessEnvironment, variableResolver?: (str: string) => string): void {
|
||||
async applyToProcessEnvironment(env: IProcessEnvironment, variableResolver?: VariableResolver): Promise<void> {
|
||||
let lowerToActualVariableNames: { [lowerKey: string]: string | undefined } | undefined;
|
||||
if (isWindows) {
|
||||
lowerToActualVariableNames = {};
|
||||
Object.keys(env).forEach(e => lowerToActualVariableNames![e.toLowerCase()] = e);
|
||||
}
|
||||
this.map.forEach((mutators, variable) => {
|
||||
for (const [variable, mutators] of this.map) {
|
||||
const actualVariable = isWindows ? lowerToActualVariableNames![variable.toLowerCase()] || variable : variable;
|
||||
mutators.forEach(mutator => {
|
||||
const value = variableResolver ? variableResolver(mutator.value) : mutator.value;
|
||||
for (const mutator of mutators) {
|
||||
const value = variableResolver ? await variableResolver(mutator.value) : mutator.value;
|
||||
switch (mutator.type) {
|
||||
case EnvironmentVariableMutatorType.Append:
|
||||
env[actualVariable] = (env[actualVariable] || '') + value;
|
||||
|
@ -62,8 +63,8 @@ export class MergedEnvironmentVariableCollection implements IMergedEnvironmentVa
|
|||
env[actualVariable] = value;
|
||||
break;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
diff(other: IMergedEnvironmentVariableCollection): IMergedEnvironmentVariableCollectionDiff | undefined {
|
||||
|
|
|
@ -78,17 +78,17 @@ function mergeNonNullKeys(env: IProcessEnvironment, other: ITerminalEnvironment
|
|||
}
|
||||
}
|
||||
|
||||
function resolveConfigurationVariables(variableResolver: VariableResolver, env: ITerminalEnvironment): ITerminalEnvironment {
|
||||
Object.keys(env).forEach((key) => {
|
||||
const value = env[key];
|
||||
async function resolveConfigurationVariables(variableResolver: VariableResolver, env: ITerminalEnvironment): Promise<ITerminalEnvironment> {
|
||||
await Promise.all(Object.entries(env).map(async ([key, value]) => {
|
||||
if (typeof value === 'string') {
|
||||
try {
|
||||
env[key] = variableResolver(value);
|
||||
env[key] = await variableResolver(value);
|
||||
} catch (e) {
|
||||
env[key] = value;
|
||||
}
|
||||
}
|
||||
});
|
||||
}));
|
||||
|
||||
return env;
|
||||
}
|
||||
|
||||
|
@ -179,17 +179,17 @@ export function getLangEnvVariable(locale?: string): string {
|
|||
return parts.join('_') + '.UTF-8';
|
||||
}
|
||||
|
||||
export function getCwd(
|
||||
export async function getCwd(
|
||||
shell: IShellLaunchConfig,
|
||||
userHome: string | undefined,
|
||||
variableResolver: VariableResolver | undefined,
|
||||
root: Uri | undefined,
|
||||
customCwd: string | undefined,
|
||||
logService?: ILogService
|
||||
): string {
|
||||
): Promise<string> {
|
||||
if (shell.cwd) {
|
||||
const unresolved = (typeof shell.cwd === 'object') ? shell.cwd.fsPath : shell.cwd;
|
||||
const resolved = _resolveCwd(unresolved, variableResolver);
|
||||
const resolved = await _resolveCwd(unresolved, variableResolver);
|
||||
return _sanitizeCwd(resolved || unresolved);
|
||||
}
|
||||
|
||||
|
@ -197,7 +197,7 @@ export function getCwd(
|
|||
|
||||
if (!shell.ignoreConfigurationCwd && customCwd) {
|
||||
if (variableResolver) {
|
||||
customCwd = _resolveCwd(customCwd, variableResolver, logService);
|
||||
customCwd = await _resolveCwd(customCwd, variableResolver, logService);
|
||||
}
|
||||
if (customCwd) {
|
||||
if (path.isAbsolute(customCwd)) {
|
||||
|
@ -216,10 +216,10 @@ export function getCwd(
|
|||
return _sanitizeCwd(cwd);
|
||||
}
|
||||
|
||||
function _resolveCwd(cwd: string, variableResolver: VariableResolver | undefined, logService?: ILogService): string | undefined {
|
||||
async function _resolveCwd(cwd: string, variableResolver: VariableResolver | undefined, logService?: ILogService): Promise<string | undefined> {
|
||||
if (variableResolver) {
|
||||
try {
|
||||
return variableResolver(cwd);
|
||||
return await variableResolver(cwd);
|
||||
} catch (e) {
|
||||
logService?.error('Could not resolve terminal cwd', e);
|
||||
return undefined;
|
||||
|
@ -251,7 +251,7 @@ export type TerminalShellArgsSetting = (
|
|||
| TerminalSettingId.ShellArgsLinux
|
||||
);
|
||||
|
||||
export type VariableResolver = (str: string) => string;
|
||||
export type VariableResolver = (str: string) => Promise<string>;
|
||||
|
||||
export function createVariableResolver(lastActiveWorkspace: IWorkspaceFolder | undefined, env: IProcessEnvironment, configurationResolverService: IConfigurationResolverService | undefined): VariableResolver | undefined {
|
||||
if (!configurationResolverService) {
|
||||
|
@ -263,7 +263,7 @@ export function createVariableResolver(lastActiveWorkspace: IWorkspaceFolder | u
|
|||
/**
|
||||
* @deprecated Use ITerminalProfileResolverService
|
||||
*/
|
||||
export function getDefaultShell(
|
||||
export async function getDefaultShell(
|
||||
fetchSetting: (key: TerminalShellSetting) => string | undefined,
|
||||
defaultShell: string,
|
||||
isWoW64: boolean,
|
||||
|
@ -272,7 +272,7 @@ export function getDefaultShell(
|
|||
logService: ILogService,
|
||||
useAutomationShell: boolean,
|
||||
platformOverride: Platform = platform
|
||||
): string {
|
||||
): Promise<string> {
|
||||
let maybeExecutable: string | undefined;
|
||||
if (useAutomationShell) {
|
||||
// If automationShell is specified, this should override the normal setting
|
||||
|
@ -300,7 +300,7 @@ export function getDefaultShell(
|
|||
|
||||
if (variableResolver) {
|
||||
try {
|
||||
executable = variableResolver(executable);
|
||||
executable = await variableResolver(executable);
|
||||
} catch (e) {
|
||||
logService.error(`Could not resolve shell`, e);
|
||||
}
|
||||
|
@ -312,13 +312,13 @@ export function getDefaultShell(
|
|||
/**
|
||||
* @deprecated Use ITerminalProfileResolverService
|
||||
*/
|
||||
export function getDefaultShellArgs(
|
||||
export async function getDefaultShellArgs(
|
||||
fetchSetting: (key: TerminalShellSetting | TerminalShellArgsSetting) => string | string[] | undefined,
|
||||
useAutomationShell: boolean,
|
||||
variableResolver: VariableResolver | undefined,
|
||||
logService: ILogService,
|
||||
platformOverride: Platform = platform,
|
||||
): string | string[] {
|
||||
): Promise<string | string[]> {
|
||||
if (useAutomationShell) {
|
||||
if (!!getShellSetting(fetchSetting, 'automationShell', platformOverride)) {
|
||||
return [];
|
||||
|
@ -331,13 +331,13 @@ export function getDefaultShellArgs(
|
|||
return [];
|
||||
}
|
||||
if (typeof args === 'string' && platformOverride === Platform.Windows) {
|
||||
return variableResolver ? variableResolver(args) : args;
|
||||
return variableResolver ? await variableResolver(args) : args;
|
||||
}
|
||||
if (variableResolver) {
|
||||
const resolvedArgs: string[] = [];
|
||||
for (const arg of args) {
|
||||
try {
|
||||
resolvedArgs.push(variableResolver(arg));
|
||||
resolvedArgs.push(await variableResolver(arg));
|
||||
} catch (e) {
|
||||
logService.error(`Could not resolve ${TerminalSettingPrefix.ShellArgs}${platformKey}`, e);
|
||||
resolvedArgs.push(arg);
|
||||
|
@ -357,14 +357,14 @@ function getShellSetting(
|
|||
return fetchSetting(<TerminalShellSetting>`terminal.integrated.${type}.${platformKey}`);
|
||||
}
|
||||
|
||||
export function createTerminalEnvironment(
|
||||
export async function createTerminalEnvironment(
|
||||
shellLaunchConfig: IShellLaunchConfig,
|
||||
envFromConfig: ITerminalEnvironment | undefined,
|
||||
variableResolver: VariableResolver | undefined,
|
||||
version: string | undefined,
|
||||
detectLocale: 'auto' | 'off' | 'on',
|
||||
baseEnv: IProcessEnvironment
|
||||
): IProcessEnvironment {
|
||||
): Promise<IProcessEnvironment> {
|
||||
// Create a terminal environment based on settings, launch config and permissions
|
||||
const env: IProcessEnvironment = {};
|
||||
if (shellLaunchConfig.strictEnv) {
|
||||
|
@ -379,10 +379,10 @@ export function createTerminalEnvironment(
|
|||
// Resolve env vars from config and shell
|
||||
if (variableResolver) {
|
||||
if (allowedEnvFromConfig) {
|
||||
resolveConfigurationVariables(variableResolver, allowedEnvFromConfig);
|
||||
await resolveConfigurationVariables(variableResolver, allowedEnvFromConfig);
|
||||
}
|
||||
if (shellLaunchConfig.env) {
|
||||
resolveConfigurationVariables(variableResolver, shellLaunchConfig.env);
|
||||
await resolveConfigurationVariables(variableResolver, shellLaunchConfig.env);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -250,9 +250,9 @@ class LocalTerminalBackend extends BaseTerminalBackend implements ITerminalBacke
|
|||
const platformKey = isWindows ? 'windows' : (isMacintosh ? 'osx' : 'linux');
|
||||
const envFromConfigValue = this._configurationService.getValue<ITerminalEnvironment | undefined>(`terminal.integrated.env.${platformKey}`);
|
||||
const baseEnv = await (shellLaunchConfig.useShellEnvironment ? this.getShellEnvironment() : this.getEnvironment());
|
||||
const env = terminalEnvironment.createTerminalEnvironment(shellLaunchConfig, envFromConfigValue, variableResolver, this._productService.version, this._configurationService.getValue(TerminalSettingId.DetectLocale), baseEnv);
|
||||
const env = await terminalEnvironment.createTerminalEnvironment(shellLaunchConfig, envFromConfigValue, variableResolver, this._productService.version, this._configurationService.getValue(TerminalSettingId.DetectLocale), baseEnv);
|
||||
if (!shellLaunchConfig.strictEnv && !shellLaunchConfig.hideFromUser) {
|
||||
this._environmentVariableService.mergedCollection.applyToProcessEnvironment(env, variableResolver);
|
||||
await this._environmentVariableService.mergedCollection.applyToProcessEnvironment(env, variableResolver);
|
||||
}
|
||||
return env;
|
||||
}
|
||||
|
|
|
@ -78,7 +78,7 @@ suite('EnvironmentVariable - MergedEnvironmentVariableCollection', () => {
|
|||
});
|
||||
|
||||
suite('applyToProcessEnvironment', () => {
|
||||
test('should apply the collection to an environment', () => {
|
||||
test('should apply the collection to an environment', async () => {
|
||||
const merged = new MergedEnvironmentVariableCollection(new Map([
|
||||
['ext', {
|
||||
map: deserializeEnvironmentVariableCollection([
|
||||
|
@ -93,7 +93,7 @@ suite('EnvironmentVariable - MergedEnvironmentVariableCollection', () => {
|
|||
B: 'bar',
|
||||
C: 'baz'
|
||||
};
|
||||
merged.applyToProcessEnvironment(env);
|
||||
await merged.applyToProcessEnvironment(env);
|
||||
deepStrictEqual(env, {
|
||||
A: 'a',
|
||||
B: 'barb',
|
||||
|
@ -101,7 +101,7 @@ suite('EnvironmentVariable - MergedEnvironmentVariableCollection', () => {
|
|||
});
|
||||
});
|
||||
|
||||
test('should apply the collection to environment entries with no values', () => {
|
||||
test('should apply the collection to environment entries with no values', async () => {
|
||||
const merged = new MergedEnvironmentVariableCollection(new Map([
|
||||
['ext', {
|
||||
map: deserializeEnvironmentVariableCollection([
|
||||
|
@ -112,7 +112,7 @@ suite('EnvironmentVariable - MergedEnvironmentVariableCollection', () => {
|
|||
}]
|
||||
]));
|
||||
const env: IProcessEnvironment = {};
|
||||
merged.applyToProcessEnvironment(env);
|
||||
await merged.applyToProcessEnvironment(env);
|
||||
deepStrictEqual(env, {
|
||||
A: 'a',
|
||||
B: 'b',
|
||||
|
@ -120,7 +120,7 @@ suite('EnvironmentVariable - MergedEnvironmentVariableCollection', () => {
|
|||
});
|
||||
});
|
||||
|
||||
test('should apply to variable case insensitively on Windows only', () => {
|
||||
test('should apply to variable case insensitively on Windows only', async () => {
|
||||
const merged = new MergedEnvironmentVariableCollection(new Map([
|
||||
['ext', {
|
||||
map: deserializeEnvironmentVariableCollection([
|
||||
|
@ -135,7 +135,7 @@ suite('EnvironmentVariable - MergedEnvironmentVariableCollection', () => {
|
|||
B: 'B',
|
||||
C: 'C'
|
||||
};
|
||||
merged.applyToProcessEnvironment(env);
|
||||
await merged.applyToProcessEnvironment(env);
|
||||
if (isWindows) {
|
||||
deepStrictEqual(env, {
|
||||
A: 'a',
|
||||
|
|
|
@ -87,7 +87,7 @@ suite('EnvironmentVariable - EnvironmentVariableService', () => {
|
|||
]);
|
||||
});
|
||||
|
||||
test('should correctly apply the environment values from multiple extension contributions in the correct order', () => {
|
||||
test('should correctly apply the environment values from multiple extension contributions in the correct order', async () => {
|
||||
const collection1 = new Map<string, IEnvironmentVariableMutator>();
|
||||
const collection2 = new Map<string, IEnvironmentVariableMutator>();
|
||||
const collection3 = new Map<string, IEnvironmentVariableMutator>();
|
||||
|
@ -109,7 +109,7 @@ suite('EnvironmentVariable - EnvironmentVariableService', () => {
|
|||
|
||||
// Verify the entries get applied to the environment as expected
|
||||
const env: IProcessEnvironment = { A: 'foo' };
|
||||
environmentVariableService.mergedCollection.applyToProcessEnvironment(env);
|
||||
await environmentVariableService.mergedCollection.applyToProcessEnvironment(env);
|
||||
deepStrictEqual(env, { A: 'a2:a3:a1' });
|
||||
});
|
||||
});
|
||||
|
|
|
@ -180,66 +180,66 @@ suite('Workbench - TerminalEnvironment', () => {
|
|||
strictEqual(Uri.file(a).fsPath, Uri.file(b).fsPath);
|
||||
}
|
||||
|
||||
test('should default to userHome for an empty workspace', () => {
|
||||
assertPathsMatch(getCwd({ executable: undefined, args: [] }, '/userHome/', undefined, undefined, undefined), '/userHome/');
|
||||
test('should default to userHome for an empty workspace', async () => {
|
||||
assertPathsMatch(await getCwd({ executable: undefined, args: [] }, '/userHome/', undefined, undefined, undefined), '/userHome/');
|
||||
});
|
||||
|
||||
test('should use to the workspace if it exists', () => {
|
||||
assertPathsMatch(getCwd({ executable: undefined, args: [] }, '/userHome/', undefined, Uri.file('/foo'), undefined), '/foo');
|
||||
test('should use to the workspace if it exists', async () => {
|
||||
assertPathsMatch(await getCwd({ executable: undefined, args: [] }, '/userHome/', undefined, Uri.file('/foo'), undefined), '/foo');
|
||||
});
|
||||
|
||||
test('should use an absolute custom cwd as is', () => {
|
||||
assertPathsMatch(getCwd({ executable: undefined, args: [] }, '/userHome/', undefined, undefined, '/foo'), '/foo');
|
||||
test('should use an absolute custom cwd as is', async () => {
|
||||
assertPathsMatch(await getCwd({ executable: undefined, args: [] }, '/userHome/', undefined, undefined, '/foo'), '/foo');
|
||||
});
|
||||
|
||||
test('should normalize a relative custom cwd against the workspace path', () => {
|
||||
assertPathsMatch(getCwd({ executable: undefined, args: [] }, '/userHome/', undefined, Uri.file('/bar'), 'foo'), '/bar/foo');
|
||||
assertPathsMatch(getCwd({ executable: undefined, args: [] }, '/userHome/', undefined, Uri.file('/bar'), './foo'), '/bar/foo');
|
||||
assertPathsMatch(getCwd({ executable: undefined, args: [] }, '/userHome/', undefined, Uri.file('/bar'), '../foo'), '/foo');
|
||||
test('should normalize a relative custom cwd against the workspace path', async () => {
|
||||
assertPathsMatch(await getCwd({ executable: undefined, args: [] }, '/userHome/', undefined, Uri.file('/bar'), 'foo'), '/bar/foo');
|
||||
assertPathsMatch(await getCwd({ executable: undefined, args: [] }, '/userHome/', undefined, Uri.file('/bar'), './foo'), '/bar/foo');
|
||||
assertPathsMatch(await getCwd({ executable: undefined, args: [] }, '/userHome/', undefined, Uri.file('/bar'), '../foo'), '/foo');
|
||||
});
|
||||
|
||||
test('should fall back for relative a custom cwd that doesn\'t have a workspace', () => {
|
||||
assertPathsMatch(getCwd({ executable: undefined, args: [] }, '/userHome/', undefined, undefined, 'foo'), '/userHome/');
|
||||
assertPathsMatch(getCwd({ executable: undefined, args: [] }, '/userHome/', undefined, undefined, './foo'), '/userHome/');
|
||||
assertPathsMatch(getCwd({ executable: undefined, args: [] }, '/userHome/', undefined, undefined, '../foo'), '/userHome/');
|
||||
test('should fall back for relative a custom cwd that doesn\'t have a workspace', async () => {
|
||||
assertPathsMatch(await getCwd({ executable: undefined, args: [] }, '/userHome/', undefined, undefined, 'foo'), '/userHome/');
|
||||
assertPathsMatch(await getCwd({ executable: undefined, args: [] }, '/userHome/', undefined, undefined, './foo'), '/userHome/');
|
||||
assertPathsMatch(await getCwd({ executable: undefined, args: [] }, '/userHome/', undefined, undefined, '../foo'), '/userHome/');
|
||||
});
|
||||
|
||||
test('should ignore custom cwd when told to ignore', () => {
|
||||
assertPathsMatch(getCwd({ executable: undefined, args: [], ignoreConfigurationCwd: true }, '/userHome/', undefined, Uri.file('/bar'), '/foo'), '/bar');
|
||||
test('should ignore custom cwd when told to ignore', async () => {
|
||||
assertPathsMatch(await getCwd({ executable: undefined, args: [], ignoreConfigurationCwd: true }, '/userHome/', undefined, Uri.file('/bar'), '/foo'), '/bar');
|
||||
});
|
||||
});
|
||||
|
||||
suite('getDefaultShell', () => {
|
||||
test('should change Sysnative to System32 in non-WoW64 systems', () => {
|
||||
const shell = getDefaultShell(key => {
|
||||
test('should change Sysnative to System32 in non-WoW64 systems', async () => {
|
||||
const shell = await getDefaultShell(key => {
|
||||
return ({ 'terminal.integrated.shell.windows': 'C:\\Windows\\Sysnative\\cmd.exe' } as any)[key];
|
||||
}, 'DEFAULT', false, 'C:\\Windows', undefined, {} as any, false, Platform.Windows);
|
||||
strictEqual(shell, 'C:\\Windows\\System32\\cmd.exe');
|
||||
});
|
||||
|
||||
test('should not change Sysnative to System32 in WoW64 systems', () => {
|
||||
const shell = getDefaultShell(key => {
|
||||
test('should not change Sysnative to System32 in WoW64 systems', async () => {
|
||||
const shell = await getDefaultShell(key => {
|
||||
return ({ 'terminal.integrated.shell.windows': 'C:\\Windows\\Sysnative\\cmd.exe' } as any)[key];
|
||||
}, 'DEFAULT', true, 'C:\\Windows', undefined, {} as any, false, Platform.Windows);
|
||||
strictEqual(shell, 'C:\\Windows\\Sysnative\\cmd.exe');
|
||||
});
|
||||
|
||||
test('should use automationShell when specified', () => {
|
||||
const shell1 = getDefaultShell(key => {
|
||||
test('should use automationShell when specified', async () => {
|
||||
const shell1 = await getDefaultShell(key => {
|
||||
return ({
|
||||
'terminal.integrated.shell.windows': 'shell',
|
||||
'terminal.integrated.automationShell.windows': undefined
|
||||
} as any)[key];
|
||||
}, 'DEFAULT', false, 'C:\\Windows', undefined, {} as any, false, Platform.Windows);
|
||||
strictEqual(shell1, 'shell', 'automationShell was false');
|
||||
const shell2 = getDefaultShell(key => {
|
||||
const shell2 = await getDefaultShell(key => {
|
||||
return ({
|
||||
'terminal.integrated.shell.windows': 'shell',
|
||||
'terminal.integrated.automationShell.windows': undefined
|
||||
} as any)[key];
|
||||
}, 'DEFAULT', false, 'C:\\Windows', undefined, {} as any, true, Platform.Windows);
|
||||
strictEqual(shell2, 'shell', 'automationShell was true');
|
||||
const shell3 = getDefaultShell(key => {
|
||||
const shell3 = await getDefaultShell(key => {
|
||||
return ({
|
||||
'terminal.integrated.shell.windows': 'shell',
|
||||
'terminal.integrated.automationShell.windows': 'automationShell'
|
||||
|
|
|
@ -19,6 +19,7 @@ import { ConfiguredInput } from 'vs/workbench/services/configurationResolver/com
|
|||
import { IProcessEnvironment } from 'vs/base/common/platform';
|
||||
import { ILabelService } from 'vs/platform/label/common/label';
|
||||
import { IPathService } from 'vs/workbench/services/path/common/pathService';
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
|
||||
export abstract class BaseConfigurationResolverService extends AbstractVariableResolverService {
|
||||
|
||||
|
@ -36,7 +37,8 @@ export abstract class BaseConfigurationResolverService extends AbstractVariableR
|
|||
private readonly workspaceContextService: IWorkspaceContextService,
|
||||
private readonly quickInputService: IQuickInputService,
|
||||
private readonly labelService: ILabelService,
|
||||
private readonly pathService: IPathService
|
||||
private readonly pathService: IPathService,
|
||||
extensionService: IExtensionService,
|
||||
) {
|
||||
super({
|
||||
getFolderUri: (folderName: string): uri | undefined => {
|
||||
|
@ -109,7 +111,10 @@ export abstract class BaseConfigurationResolverService extends AbstractVariableR
|
|||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
},
|
||||
getExtension: id => {
|
||||
return extensionService.getExtension(id);
|
||||
},
|
||||
}, labelService, pathService.userHome().then(home => home.path), envVariablesPromise);
|
||||
}
|
||||
|
||||
|
|
|
@ -3,16 +3,17 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
|
||||
import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver';
|
||||
import { ILabelService } from 'vs/platform/label/common/label';
|
||||
import { IPathService } from 'vs/workbench/services/path/common/pathService';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
import { ILabelService } from 'vs/platform/label/common/label';
|
||||
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
|
||||
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
import { BaseConfigurationResolverService } from 'vs/workbench/services/configurationResolver/browser/baseConfigurationResolverService';
|
||||
import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { IPathService } from 'vs/workbench/services/path/common/pathService';
|
||||
|
||||
export class ConfigurationResolverService extends BaseConfigurationResolverService {
|
||||
|
||||
|
@ -23,11 +24,12 @@ export class ConfigurationResolverService extends BaseConfigurationResolverServi
|
|||
@IWorkspaceContextService workspaceContextService: IWorkspaceContextService,
|
||||
@IQuickInputService quickInputService: IQuickInputService,
|
||||
@ILabelService labelService: ILabelService,
|
||||
@IPathService pathService: IPathService
|
||||
@IPathService pathService: IPathService,
|
||||
@IExtensionService extensionService: IExtensionService,
|
||||
) {
|
||||
super({ getAppRoot: () => undefined, getExecPath: () => undefined },
|
||||
Promise.resolve(Object.create(null)), editorService, configurationService,
|
||||
commandService, workspaceContextService, quickInputService, labelService, pathService);
|
||||
commandService, workspaceContextService, quickInputService, labelService, pathService, extensionService);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ export const IConfigurationResolverService = createDecorator<IConfigurationResol
|
|||
export interface IConfigurationResolverService {
|
||||
readonly _serviceBrand: undefined;
|
||||
|
||||
resolveWithEnvironment(environment: IProcessEnvironment, folder: IWorkspaceFolder | undefined, value: string): string;
|
||||
resolveWithEnvironment(environment: IProcessEnvironment, folder: IWorkspaceFolder | undefined, value: string): Promise<string>;
|
||||
|
||||
resolveAsync(folder: IWorkspaceFolder | undefined, value: string): Promise<string>;
|
||||
resolveAsync(folder: IWorkspaceFolder | undefined, value: string[]): Promise<string[]>;
|
||||
|
|
|
@ -15,6 +15,7 @@ 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';
|
||||
import { replaceAsync } from 'vs/base/common/strings';
|
||||
|
||||
export interface IVariableResolveContext {
|
||||
getFolderUri(folderName: string): uri | undefined;
|
||||
|
@ -26,6 +27,7 @@ export interface IVariableResolveContext {
|
|||
getWorkspaceFolderPathForFile?(): string | undefined;
|
||||
getSelectedText(): string | undefined;
|
||||
getLineNumber(): string | undefined;
|
||||
getExtension(id: string): Promise<{ readonly extensionLocation: uri } | undefined>;
|
||||
}
|
||||
|
||||
type Environment = { env: IProcessEnvironment | undefined; userHome: string | undefined };
|
||||
|
@ -66,7 +68,7 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe
|
|||
return envVariables;
|
||||
}
|
||||
|
||||
public resolveWithEnvironment(environment: IProcessEnvironment, root: IWorkspaceFolder | undefined, value: string): string {
|
||||
public resolveWithEnvironment(environment: IProcessEnvironment, root: IWorkspaceFolder | undefined, value: string): Promise<string> {
|
||||
return this.recursiveResolve({ env: this.prepareEnv(environment), userHome: undefined }, root ? root.uri : undefined, value);
|
||||
}
|
||||
|
||||
|
@ -133,52 +135,53 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe
|
|||
}
|
||||
}
|
||||
|
||||
private recursiveResolve(environment: Environment, folderUri: uri | undefined, value: any, commandValueMapping?: IStringDictionary<string>, resolvedVariables?: Map<string, string>): any {
|
||||
private async recursiveResolve(environment: Environment, folderUri: uri | undefined, value: any, commandValueMapping?: IStringDictionary<string>, resolvedVariables?: Map<string, string>): Promise<any> {
|
||||
if (types.isString(value)) {
|
||||
return this.resolveString(environment, folderUri, value, commandValueMapping, resolvedVariables);
|
||||
} else if (types.isArray(value)) {
|
||||
return value.map(s => this.recursiveResolve(environment, folderUri, s, commandValueMapping, resolvedVariables));
|
||||
return Promise.all(value.map(s => this.recursiveResolve(environment, folderUri, s, commandValueMapping, resolvedVariables)));
|
||||
} else if (types.isObject(value)) {
|
||||
let result: IStringDictionary<string | IStringDictionary<string> | string[]> = Object.create(null);
|
||||
Object.keys(value).forEach(key => {
|
||||
const replaced = this.resolveString(environment, folderUri, key, commandValueMapping, resolvedVariables);
|
||||
result[replaced] = this.recursiveResolve(environment, folderUri, value[key], commandValueMapping, resolvedVariables);
|
||||
});
|
||||
const replaced = await Promise.all(Object.keys(value).map(async key => {
|
||||
const replaced = await this.resolveString(environment, folderUri, key, commandValueMapping, resolvedVariables);
|
||||
return [replaced, await this.recursiveResolve(environment, folderUri, value[key], commandValueMapping, resolvedVariables)] as const;
|
||||
}));
|
||||
// two step process to preserve object key order
|
||||
for (const [key, value] of replaced) {
|
||||
result[key] = value;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
private resolveString(environment: Environment, folderUri: uri | undefined, value: string, commandValueMapping: IStringDictionary<string> | undefined, resolvedVariables?: Map<string, string>): string {
|
||||
|
||||
private resolveString(environment: Environment, folderUri: uri | undefined, value: string, commandValueMapping: IStringDictionary<string> | undefined, resolvedVariables?: Map<string, string>): Promise<string> {
|
||||
// loop through all variables occurrences in 'value'
|
||||
const replaced = value.replace(AbstractVariableResolverService.VARIABLE_REGEXP, (match: string, variable: string) => {
|
||||
return replaceAsync(value, AbstractVariableResolverService.VARIABLE_REGEXP, async (match: string, variable: string) => {
|
||||
// disallow attempted nesting, see #77289. This doesn't exclude variables that resolve to other variables.
|
||||
if (variable.includes(AbstractVariableResolverService.VARIABLE_LHS)) {
|
||||
return match;
|
||||
}
|
||||
|
||||
let resolvedValue = this.evaluateSingleVariable(environment, match, variable, folderUri, commandValueMapping);
|
||||
let resolvedValue = await this.evaluateSingleVariable(environment, match, variable, folderUri, commandValueMapping);
|
||||
|
||||
if (resolvedVariables) {
|
||||
resolvedVariables.set(variable, resolvedValue);
|
||||
}
|
||||
|
||||
if ((resolvedValue !== match) && types.isString(resolvedValue) && resolvedValue.match(AbstractVariableResolverService.VARIABLE_REGEXP)) {
|
||||
resolvedValue = this.resolveString(environment, folderUri, resolvedValue, commandValueMapping, resolvedVariables);
|
||||
resolvedValue = await this.resolveString(environment, folderUri, resolvedValue, commandValueMapping, resolvedVariables);
|
||||
}
|
||||
|
||||
return resolvedValue;
|
||||
});
|
||||
|
||||
return replaced;
|
||||
}
|
||||
|
||||
private fsPath(displayUri: uri): string {
|
||||
return this._labelService ? this._labelService.getUriLabel(displayUri, { noPrefix: true }) : displayUri.fsPath;
|
||||
}
|
||||
|
||||
private evaluateSingleVariable(environment: Environment, match: string, variable: string, folderUri: uri | undefined, commandValueMapping: IStringDictionary<string> | undefined): string {
|
||||
private async evaluateSingleVariable(environment: Environment, match: string, variable: string, folderUri: uri | undefined, commandValueMapping: IStringDictionary<string> | undefined): Promise<string> {
|
||||
|
||||
// try to separate variable arguments from variable name
|
||||
let argument: string | undefined;
|
||||
|
@ -268,6 +271,16 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe
|
|||
case 'input':
|
||||
return this.resolveFromMap(match, argument, commandValueMapping, 'input');
|
||||
|
||||
case 'extensionInstallFolder':
|
||||
if (argument) {
|
||||
const ext = await this._context.getExtension(argument);
|
||||
if (!ext) {
|
||||
throw new Error(localize('extensionNotInstalled', "Variable {0} can not be resolved because the extension {1} is not installed.", match, argument));
|
||||
}
|
||||
return this.fsPath(ext.extensionLocation);
|
||||
}
|
||||
throw new Error(localize('missingExtensionName', "Variable {0} can not be resolved because no extension name is given.", match));
|
||||
|
||||
default: {
|
||||
|
||||
switch (variable) {
|
||||
|
|
|
@ -15,6 +15,7 @@ import { BaseConfigurationResolverService } from 'vs/workbench/services/configur
|
|||
import { ILabelService } from 'vs/platform/label/common/label';
|
||||
import { IShellEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/shellEnvironmentService';
|
||||
import { IPathService } from 'vs/workbench/services/path/common/pathService';
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
|
||||
export class ConfigurationResolverService extends BaseConfigurationResolverService {
|
||||
|
||||
|
@ -27,7 +28,8 @@ export class ConfigurationResolverService extends BaseConfigurationResolverServi
|
|||
@IQuickInputService quickInputService: IQuickInputService,
|
||||
@ILabelService labelService: ILabelService,
|
||||
@IShellEnvironmentService shellEnvironmentService: IShellEnvironmentService,
|
||||
@IPathService pathService: IPathService
|
||||
@IPathService pathService: IPathService,
|
||||
@IExtensionService extensionService: IExtensionService,
|
||||
) {
|
||||
super({
|
||||
getAppRoot: (): string | undefined => {
|
||||
|
@ -35,9 +37,9 @@ export class ConfigurationResolverService extends BaseConfigurationResolverServi
|
|||
},
|
||||
getExecPath: (): string | undefined => {
|
||||
return environmentService.execPath;
|
||||
}
|
||||
},
|
||||
}, shellEnvironmentService.getShellEnv(), editorService, configurationService, commandService,
|
||||
workspaceContextService, quickInputService, labelService, pathService);
|
||||
workspaceContextService, quickInputService, labelService, pathService, extensionService);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { stub } from 'sinon';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
|
@ -16,15 +17,17 @@ import { EditorType } from 'vs/editor/common/editorCommon';
|
|||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
|
||||
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { IFormatterChangeEvent, ILabelService, ResourceLabelFormatter } from 'vs/platform/label/common/label';
|
||||
import { IWorkspace, IWorkspaceFolder, IWorkspaceIdentifier, Workspace } from 'vs/platform/workspace/common/workspace';
|
||||
import { testWorkspace } from 'vs/platform/workspace/test/common/testWorkspace';
|
||||
import { BaseConfigurationResolverService } from 'vs/workbench/services/configurationResolver/browser/baseConfigurationResolverService';
|
||||
import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver';
|
||||
import { NativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/environmentService';
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { IPathService } from 'vs/workbench/services/path/common/pathService';
|
||||
import { TestEditorService, TestQuickInputService } from 'vs/workbench/test/browser/workbenchTestServices';
|
||||
import { TestContextService, TestProductService } from 'vs/workbench/test/common/workbenchTestServices';
|
||||
import { TestContextService, TestExtensionService, TestProductService } from 'vs/workbench/test/common/workbenchTestServices';
|
||||
import { TestNativeWindowConfiguration } from 'vs/workbench/test/electron-browser/workbenchTestServices';
|
||||
|
||||
const mockLineNumber = 10;
|
||||
|
@ -68,6 +71,7 @@ suite('Configuration Resolver Service', () => {
|
|||
let quickInputService: TestQuickInputService;
|
||||
let labelService: MockLabelService;
|
||||
let pathService: MockPathService;
|
||||
let extensionService: IExtensionService;
|
||||
|
||||
setup(() => {
|
||||
mockCommandService = new MockCommandService();
|
||||
|
@ -76,9 +80,10 @@ suite('Configuration Resolver Service', () => {
|
|||
environmentService = new MockWorkbenchEnvironmentService(envVariables);
|
||||
labelService = new MockLabelService();
|
||||
pathService = new MockPathService();
|
||||
extensionService = new TestExtensionService();
|
||||
containingWorkspace = testWorkspace(uri.parse('file:///VSCode/workspaceLocation'));
|
||||
workspace = containingWorkspace.folders[0];
|
||||
configurationResolverService = new TestConfigurationResolverService(nullContext, Promise.resolve(environmentService.userEnv), editorService, new MockInputsConfigurationService(), mockCommandService, new TestContextService(containingWorkspace), quickInputService, labelService, pathService);
|
||||
configurationResolverService = new TestConfigurationResolverService(nullContext, Promise.resolve(environmentService.userEnv), editorService, new MockInputsConfigurationService(), mockCommandService, new TestContextService(containingWorkspace), quickInputService, labelService, pathService, extensionService);
|
||||
});
|
||||
|
||||
teardown(() => {
|
||||
|
@ -185,6 +190,13 @@ suite('Configuration Resolver Service', () => {
|
|||
assert.strictEqual(await configurationResolverService!.resolveAsync(workspace, '${env:key1} ${env:key1${env:key2}}'), 'Value for key1 ${env:key1${env:key2}}');
|
||||
});
|
||||
|
||||
test('supports extensionDir', async () => {
|
||||
const getExtension = stub(extensionService, 'getExtension');
|
||||
getExtension.withArgs('publisher.extId').returns(Promise.resolve({ extensionLocation: uri.file('/some/path') } as IExtensionDescription));
|
||||
|
||||
assert.strictEqual(await configurationResolverService!.resolveAsync(workspace, '${extensionInstallFolder:publisher.extId}'), uri.file('/some/path').fsPath);
|
||||
});
|
||||
|
||||
// test('substitute keys and values in object', () => {
|
||||
// const myObject = {
|
||||
// '${workspaceRootFolderName}': '${lineNumber}',
|
||||
|
@ -217,7 +229,7 @@ suite('Configuration Resolver Service', () => {
|
|||
}
|
||||
});
|
||||
|
||||
let service = new TestConfigurationResolverService(nullContext, Promise.resolve(environmentService.userEnv), new TestEditorServiceWithActiveEditor(), configurationService, mockCommandService, new TestContextService(), quickInputService, labelService, pathService);
|
||||
let service = new TestConfigurationResolverService(nullContext, Promise.resolve(environmentService.userEnv), new TestEditorServiceWithActiveEditor(), configurationService, mockCommandService, new TestContextService(), quickInputService, labelService, pathService, extensionService);
|
||||
assert.strictEqual(await service.resolveAsync(workspace, 'abc ${config:editor.fontFamily} xyz'), 'abc foo xyz');
|
||||
});
|
||||
|
||||
|
@ -228,7 +240,7 @@ suite('Configuration Resolver Service', () => {
|
|||
}
|
||||
});
|
||||
|
||||
let service = new TestConfigurationResolverService(nullContext, Promise.resolve(environmentService.userEnv), new TestEditorServiceWithActiveEditor(), configurationService, mockCommandService, new TestContextService(), quickInputService, labelService, pathService);
|
||||
let service = new TestConfigurationResolverService(nullContext, Promise.resolve(environmentService.userEnv), new TestEditorServiceWithActiveEditor(), configurationService, mockCommandService, new TestContextService(), quickInputService, labelService, pathService, extensionService);
|
||||
assert.strictEqual(await service.resolveAsync(undefined, 'abc ${config:editor.fontFamily} xyz'), 'abc foo xyz');
|
||||
});
|
||||
|
||||
|
@ -245,7 +257,7 @@ suite('Configuration Resolver Service', () => {
|
|||
}
|
||||
});
|
||||
|
||||
let service = new TestConfigurationResolverService(nullContext, Promise.resolve(environmentService.userEnv), new TestEditorServiceWithActiveEditor(), configurationService, mockCommandService, new TestContextService(), quickInputService, labelService, pathService);
|
||||
let service = new TestConfigurationResolverService(nullContext, Promise.resolve(environmentService.userEnv), new TestEditorServiceWithActiveEditor(), configurationService, mockCommandService, new TestContextService(), quickInputService, labelService, pathService, extensionService);
|
||||
assert.strictEqual(await service.resolveAsync(workspace, 'abc ${config:editor.fontFamily} ${config:terminal.integrated.fontFamily} xyz'), 'abc foo bar xyz');
|
||||
});
|
||||
|
||||
|
@ -262,7 +274,7 @@ suite('Configuration Resolver Service', () => {
|
|||
}
|
||||
});
|
||||
|
||||
let service = new TestConfigurationResolverService(nullContext, Promise.resolve(environmentService.userEnv), new TestEditorServiceWithActiveEditor(), configurationService, mockCommandService, new TestContextService(), quickInputService, labelService, pathService);
|
||||
let service = new TestConfigurationResolverService(nullContext, Promise.resolve(environmentService.userEnv), new TestEditorServiceWithActiveEditor(), configurationService, mockCommandService, new TestContextService(), quickInputService, labelService, pathService, extensionService);
|
||||
if (platform.isWindows) {
|
||||
assert.strictEqual(await service.resolveAsync(workspace, 'abc ${config:editor.fontFamily} ${workspaceFolder} ${env:key1} xyz'), 'abc foo \\VSCode\\workspaceLocation Value for key1 xyz');
|
||||
} else {
|
||||
|
@ -283,7 +295,7 @@ suite('Configuration Resolver Service', () => {
|
|||
}
|
||||
});
|
||||
|
||||
let service = new TestConfigurationResolverService(nullContext, Promise.resolve(environmentService.userEnv), new TestEditorServiceWithActiveEditor(), configurationService, mockCommandService, new TestContextService(), quickInputService, labelService, pathService);
|
||||
let service = new TestConfigurationResolverService(nullContext, Promise.resolve(environmentService.userEnv), new TestEditorServiceWithActiveEditor(), configurationService, mockCommandService, new TestContextService(), quickInputService, labelService, pathService, extensionService);
|
||||
if (platform.isWindows) {
|
||||
assert.strictEqual(await service.resolveAsync(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 {
|
||||
|
@ -317,7 +329,7 @@ suite('Configuration Resolver Service', () => {
|
|||
}
|
||||
});
|
||||
|
||||
let service = new TestConfigurationResolverService(nullContext, Promise.resolve(environmentService.userEnv), new TestEditorServiceWithActiveEditor(), configurationService, mockCommandService, new TestContextService(), quickInputService, labelService, pathService);
|
||||
let service = new TestConfigurationResolverService(nullContext, Promise.resolve(environmentService.userEnv), new TestEditorServiceWithActiveEditor(), configurationService, mockCommandService, new TestContextService(), quickInputService, labelService, pathService, extensionService);
|
||||
assert.strictEqual(await service.resolveAsync(workspace, 'abc ${config:editor.fontFamily} ${config:editor.lineNumbers} ${config:editor.insertSpaces} xyz'), 'abc foo 123 false xyz');
|
||||
});
|
||||
|
||||
|
@ -327,7 +339,7 @@ suite('Configuration Resolver Service', () => {
|
|||
editor: {}
|
||||
});
|
||||
|
||||
let service = new TestConfigurationResolverService(nullContext, Promise.resolve(environmentService.userEnv), new TestEditorServiceWithActiveEditor(), configurationService, mockCommandService, new TestContextService(), quickInputService, labelService, pathService);
|
||||
let service = new TestConfigurationResolverService(nullContext, Promise.resolve(environmentService.userEnv), new TestEditorServiceWithActiveEditor(), configurationService, mockCommandService, new TestContextService(), quickInputService, labelService, pathService, extensionService);
|
||||
assert.strictEqual(await service.resolveAsync(workspace, 'abc ${unknownVariable} xyz'), 'abc ${unknownVariable} xyz');
|
||||
assert.strictEqual(await service.resolveAsync(workspace, 'abc ${env:unknownVariable} xyz'), 'abc xyz');
|
||||
});
|
||||
|
@ -340,7 +352,7 @@ suite('Configuration Resolver Service', () => {
|
|||
}
|
||||
});
|
||||
|
||||
let service = new TestConfigurationResolverService(nullContext, Promise.resolve(environmentService.userEnv), new TestEditorServiceWithActiveEditor(), configurationService, mockCommandService, new TestContextService(), quickInputService, labelService, pathService);
|
||||
let service = new TestConfigurationResolverService(nullContext, Promise.resolve(environmentService.userEnv), new TestEditorServiceWithActiveEditor(), configurationService, mockCommandService, new TestContextService(), quickInputService, labelService, pathService, extensionService);
|
||||
|
||||
assert.rejects(async () => await service.resolveAsync(workspace, 'abc ${env} xyz'));
|
||||
assert.rejects(async () => await service.resolveAsync(workspace, 'abc ${env:} xyz'));
|
||||
|
@ -632,13 +644,13 @@ suite('Configuration Resolver Service', () => {
|
|||
});
|
||||
});
|
||||
|
||||
test('resolveWithEnvironment', () => {
|
||||
test('resolveWithEnvironment', async () => {
|
||||
const env = {
|
||||
'VAR_1': 'VAL_1',
|
||||
'VAR_2': 'VAL_2'
|
||||
};
|
||||
const configuration = 'echo ${env:VAR_1}${env:VAR_2}';
|
||||
const resolvedResult = configurationResolverService!.resolveWithEnvironment({ ...env }, undefined, configuration);
|
||||
const resolvedResult = await configurationResolverService!.resolveWithEnvironment({ ...env }, undefined, configuration);
|
||||
assert.deepStrictEqual(resolvedResult, 'echo VAL_1VAL_2');
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue
Block a user