mirror of
https://github.com/Microsoft/vscode
synced 2024-07-17 02:57:19 +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) {
|
provideCompletionItems(document, position, _token) {
|
||||||
const location = getLocation(document.getText(), document.offsetAt(position));
|
const location = getLocation(document.getText(), document.offsetAt(position));
|
||||||
if (!location.isAtPropertyKey && location.previousNode && location.previousNode.type === 'string') {
|
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;
|
const startPosition = indexOf$ >= 0 ? new vscode.Position(position.line, indexOf$) : position;
|
||||||
|
|
||||||
return [
|
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: '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: '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: '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 => ({
|
].map(variable => ({
|
||||||
label: '${' + variable.label + '}',
|
label: `\${${variable.label}}`,
|
||||||
range: new vscode.Range(startPosition, position),
|
range: new vscode.Range(startPosition, position),
|
||||||
|
insertText: variable.param ? new vscode.SnippetString(`\${${variable.label}:`).appendPlaceholder(variable.param).appendText('}') : (`\${${variable.label}}`),
|
||||||
detail: variable.detail
|
detail: variable.detail
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
|
@ -274,6 +274,29 @@ export function lastNonWhitespaceIndex(str: string, startIndex: number = str.len
|
||||||
return -1;
|
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 {
|
export function compare(a: string, b: string): number {
|
||||||
if (a < b) {
|
if (a < b) {
|
||||||
return -1;
|
return -1;
|
||||||
|
|
|
@ -386,4 +386,13 @@ suite('Strings', () => {
|
||||||
assert.strictEqual('hello world', strings.truncate('hello world', 100));
|
assert.strictEqual('hello world', strings.truncate('hello world', 100));
|
||||||
assert.strictEqual('hello…', strings.truncate('hello world', 5));
|
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 { buildUserEnvironment } from 'vs/server/node/extensionHostConnection';
|
||||||
import { IServerEnvironmentService } from 'vs/server/node/serverEnvironmentService';
|
import { IServerEnvironmentService } from 'vs/server/node/serverEnvironmentService';
|
||||||
import { IProductService } from 'vs/platform/product/common/productService';
|
import { IProductService } from 'vs/platform/product/common/productService';
|
||||||
|
import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||||
|
|
||||||
class CustomVariableResolver extends AbstractVariableResolverService {
|
class CustomVariableResolver extends AbstractVariableResolverService {
|
||||||
constructor(
|
constructor(
|
||||||
env: platform.IProcessEnvironment,
|
env: platform.IProcessEnvironment,
|
||||||
workspaceFolders: IWorkspaceFolder[],
|
workspaceFolders: IWorkspaceFolder[],
|
||||||
activeFileResource: URI | undefined,
|
activeFileResource: URI | undefined,
|
||||||
resolvedVariables: { [name: string]: string }
|
resolvedVariables: { [name: string]: string },
|
||||||
|
extensionService: IExtensionManagementService,
|
||||||
) {
|
) {
|
||||||
super({
|
super({
|
||||||
getFolderUri: (folderName: string): URI | undefined => {
|
getFolderUri: (folderName: string): URI | undefined => {
|
||||||
|
@ -68,7 +70,12 @@ class CustomVariableResolver extends AbstractVariableResolverService {
|
||||||
},
|
},
|
||||||
getLineNumber: (): string | undefined => {
|
getLineNumber: (): string | undefined => {
|
||||||
return resolvedVariables['lineNumber'];
|
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));
|
}, 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 _environmentService: IServerEnvironmentService,
|
||||||
private readonly _logService: ILogService,
|
private readonly _logService: ILogService,
|
||||||
private readonly _ptyService: IPtyService,
|
private readonly _ptyService: IPtyService,
|
||||||
private readonly _productService: IProductService
|
private readonly _productService: IProductService,
|
||||||
|
private readonly _extensionManagementService: IExtensionManagementService,
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
@ -196,16 +204,16 @@ export class RemoteTerminalChannel extends Disposable implements IServerChannel<
|
||||||
const workspaceFolders = args.workspaceFolders.map(reviveWorkspaceFolder);
|
const workspaceFolders = args.workspaceFolders.map(reviveWorkspaceFolder);
|
||||||
const activeWorkspaceFolder = args.activeWorkspaceFolder ? reviveWorkspaceFolder(args.activeWorkspaceFolder) : undefined;
|
const activeWorkspaceFolder = args.activeWorkspaceFolder ? reviveWorkspaceFolder(args.activeWorkspaceFolder) : undefined;
|
||||||
const activeFileResource = args.activeFileResource ? URI.revive(uriTransformer.transformIncoming(args.activeFileResource)) : 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);
|
const variableResolver = terminalEnvironment.createVariableResolver(activeWorkspaceFolder, process.env, customVariableResolver);
|
||||||
|
|
||||||
// Get the initial cwd
|
// 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;
|
shellLaunchConfig.cwd = initialCwd;
|
||||||
|
|
||||||
const envPlatformKey = platform.isWindows ? 'terminal.integrated.env.windows' : (platform.isMacintosh ? 'terminal.integrated.env.osx' : 'terminal.integrated.env.linux');
|
const envPlatformKey = platform.isWindows ? 'terminal.integrated.env.windows' : (platform.isMacintosh ? 'terminal.integrated.env.osx' : 'terminal.integrated.env.linux');
|
||||||
const envFromConfig = args.configuration[envPlatformKey];
|
const envFromConfig = args.configuration[envPlatformKey];
|
||||||
const env = terminalEnvironment.createTerminalEnvironment(
|
const env = await terminalEnvironment.createTerminalEnvironment(
|
||||||
shellLaunchConfig,
|
shellLaunchConfig,
|
||||||
envFromConfig,
|
envFromConfig,
|
||||||
variableResolver,
|
variableResolver,
|
||||||
|
@ -222,7 +230,7 @@ export class RemoteTerminalChannel extends Disposable implements IServerChannel<
|
||||||
}
|
}
|
||||||
const envVariableCollections = new Map<string, IEnvironmentVariableCollection>(entries);
|
const envVariableCollections = new Map<string, IEnvironmentVariableCollection>(entries);
|
||||||
const mergedCollection = new MergedEnvironmentVariableCollection(envVariableCollections);
|
const mergedCollection = new MergedEnvironmentVariableCollection(envVariableCollections);
|
||||||
mergedCollection.applyToProcessEnvironment(env);
|
await mergedCollection.applyToProcessEnvironment(env, variableResolver);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fork the process and listen for messages
|
// 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);
|
const telemetryChannel = new ServerTelemetryChannel(accessor.get(IServerTelemetryService), appInsightsAppender);
|
||||||
socketServer.registerChannel('telemetry', telemetryChannel);
|
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);
|
const remoteFileSystemChannel = new RemoteAgentFileSystemProviderChannel(logService, environmentService);
|
||||||
socketServer.registerChannel(REMOTE_FILE_SYSTEM_CHANNEL_NAME, remoteFileSystemChannel);
|
socketServer.registerChannel(REMOTE_FILE_SYSTEM_CHANNEL_NAME, remoteFileSystemChannel);
|
||||||
|
|
|
@ -57,6 +57,9 @@ export class MainThreadExtensionService implements MainThreadExtensionServiceSha
|
||||||
public dispose(): void {
|
public dispose(): void {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$getExtension(extensionId: string) {
|
||||||
|
return this._extensionService.getExtension(extensionId);
|
||||||
|
}
|
||||||
$activateExtension(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<void> {
|
$activateExtension(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<void> {
|
||||||
return this._internalExtensionService._activateById(extensionId, reason);
|
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 { ExtHostLoggerService } from 'vs/workbench/api/common/extHostLoggerService';
|
||||||
import { ILoggerService, ILogService } from 'vs/platform/log/common/log';
|
import { ILoggerService, ILogService } from 'vs/platform/log/common/log';
|
||||||
import { ExtHostLogService } from 'vs/workbench/api/common/extHostLogService';
|
import { ExtHostLogService } from 'vs/workbench/api/common/extHostLogService';
|
||||||
|
import { ExtHostVariableResolverProviderService, IExtHostVariableResolverProvider } from 'vs/workbench/api/common/extHostVariableResolverService';
|
||||||
|
|
||||||
registerSingleton(ILoggerService, ExtHostLoggerService);
|
registerSingleton(ILoggerService, ExtHostLoggerService);
|
||||||
registerSingleton(ILogService, ExtHostLogService);
|
registerSingleton(ILogService, ExtHostLogService);
|
||||||
|
@ -48,3 +49,4 @@ registerSingleton(IExtHostWorkspace, ExtHostWorkspace);
|
||||||
registerSingleton(IExtHostSecretState, ExtHostSecretState);
|
registerSingleton(IExtHostSecretState, ExtHostSecretState);
|
||||||
registerSingleton(IExtHostTelemetry, ExtHostTelemetry);
|
registerSingleton(IExtHostTelemetry, ExtHostTelemetry);
|
||||||
registerSingleton(IExtHostEditorTabs, ExtHostEditorTabs);
|
registerSingleton(IExtHostEditorTabs, ExtHostEditorTabs);
|
||||||
|
registerSingleton(IExtHostVariableResolverProvider, ExtHostVariableResolverProviderService);
|
||||||
|
|
|
@ -1107,6 +1107,7 @@ export interface MainThreadTaskShape extends IDisposable {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MainThreadExtensionServiceShape extends IDisposable {
|
export interface MainThreadExtensionServiceShape extends IDisposable {
|
||||||
|
$getExtension(extensionId: string): Promise<Dto<IExtensionDescription> | undefined>;
|
||||||
$activateExtension(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<void>;
|
$activateExtension(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<void>;
|
||||||
$onWillActivateExtension(extensionId: ExtensionIdentifier): Promise<void>;
|
$onWillActivateExtension(extensionId: ExtensionIdentifier): Promise<void>;
|
||||||
$onDidActivateExtension(extensionId: ExtensionIdentifier, codeLoadingTime: number, activateCallTime: number, activateResolvedTime: number, activationReason: ExtensionActivationReason): 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.
|
* 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 { 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 { CancellationToken } from 'vs/base/common/cancellation';
|
||||||
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry';
|
import { Emitter, Event } from 'vs/base/common/event';
|
||||||
import { ISignService } from 'vs/platform/sign/common/sign';
|
import { withNullAsUndefined } from 'vs/base/common/types';
|
||||||
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
|
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||||
import type * as vscode from 'vscode';
|
|
||||||
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||||
import { withNullAsUndefined } from 'vs/base/common/types';
|
import { ISignService } from 'vs/platform/sign/common/sign';
|
||||||
import * as process from 'vs/base/common/process';
|
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 { 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 { 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');
|
export const IExtHostDebugService = createDecorator<IExtHostDebugService>('IExtHostDebugService');
|
||||||
|
|
||||||
|
@ -101,17 +94,15 @@ export abstract class ExtHostDebugServiceBase implements IExtHostDebugService, E
|
||||||
private _debugAdapters: Map<number, IDebugAdapter>;
|
private _debugAdapters: Map<number, IDebugAdapter>;
|
||||||
private _debugAdaptersTrackers: Map<number, vscode.DebugAdapterTracker>;
|
private _debugAdaptersTrackers: Map<number, vscode.DebugAdapterTracker>;
|
||||||
|
|
||||||
private _variableResolver: IConfigurationResolverService | undefined;
|
|
||||||
|
|
||||||
private _signService: ISignService | undefined;
|
private _signService: ISignService | undefined;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@IExtHostRpcService extHostRpcService: IExtHostRpcService,
|
@IExtHostRpcService extHostRpcService: IExtHostRpcService,
|
||||||
@IExtHostWorkspace protected _workspaceService: IExtHostWorkspace,
|
@IExtHostWorkspace protected _workspaceService: IExtHostWorkspace,
|
||||||
@IExtHostExtensionService private _extensionService: IExtHostExtensionService,
|
@IExtHostExtensionService private _extensionService: IExtHostExtensionService,
|
||||||
@IExtHostDocumentsAndEditors private _editorsService: IExtHostDocumentsAndEditors,
|
|
||||||
@IExtHostConfiguration protected _configurationService: IExtHostConfiguration,
|
@IExtHostConfiguration protected _configurationService: IExtHostConfiguration,
|
||||||
@IExtHostEditorTabs protected _editorTabs: IExtHostEditorTabs
|
@IExtHostEditorTabs protected _editorTabs: IExtHostEditorTabs,
|
||||||
|
@IExtHostVariableResolverProvider private _variableResolver: IExtHostVariableResolverProvider,
|
||||||
) {
|
) {
|
||||||
this._configProviderHandleCounter = 0;
|
this._configProviderHandleCounter = 0;
|
||||||
this._configProviders = [];
|
this._configProviders = [];
|
||||||
|
@ -371,13 +362,7 @@ export abstract class ExtHostDebugServiceBase implements IExtHostDebugService, E
|
||||||
return Promise.resolve(undefined);
|
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> {
|
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;
|
let ws: IWorkspaceFolder | undefined;
|
||||||
const folder = await this.getFolder(folderUri);
|
const folder = await this.getFolder(folderUri);
|
||||||
if (folder) {
|
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 {
|
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 {
|
interface ConfigProviderTuple {
|
||||||
type: string;
|
type: string;
|
||||||
handle: number;
|
handle: number;
|
||||||
|
@ -1108,14 +1006,10 @@ export class WorkerExtHostDebugService extends ExtHostDebugServiceBase {
|
||||||
@IExtHostRpcService extHostRpcService: IExtHostRpcService,
|
@IExtHostRpcService extHostRpcService: IExtHostRpcService,
|
||||||
@IExtHostWorkspace workspaceService: IExtHostWorkspace,
|
@IExtHostWorkspace workspaceService: IExtHostWorkspace,
|
||||||
@IExtHostExtensionService extensionService: IExtHostExtensionService,
|
@IExtHostExtensionService extensionService: IExtHostExtensionService,
|
||||||
@IExtHostDocumentsAndEditors editorsService: IExtHostDocumentsAndEditors,
|
|
||||||
@IExtHostConfiguration configurationService: IExtHostConfiguration,
|
@IExtHostConfiguration configurationService: IExtHostConfiguration,
|
||||||
@IExtHostEditorTabs editorTabs: IExtHostEditorTabs
|
@IExtHostEditorTabs editorTabs: IExtHostEditorTabs,
|
||||||
|
@IExtHostVariableResolverProvider variableResolver: IExtHostVariableResolverProvider
|
||||||
) {
|
) {
|
||||||
super(extHostRpcService, workspaceService, extensionService, editorsService, configurationService, editorTabs);
|
super(extHostRpcService, workspaceService, extensionService, configurationService, editorTabs, variableResolver);
|
||||||
}
|
|
||||||
|
|
||||||
protected createVariableResolver(folders: vscode.WorkspaceFolder[], editorService: ExtHostDocumentsAndEditors, configurationService: ExtHostConfigProvider): AbstractVariableResolverService {
|
|
||||||
return new ExtHostVariableResolverService(folders, editorService, configurationService, this._editorTabs);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -229,6 +229,15 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
|
||||||
return false;
|
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> {
|
private _activateByEvent(activationEvent: string, startup: boolean): Promise<void> {
|
||||||
return this._activator.activateByEvent(activationEvent, startup);
|
return this._activator.activateByEvent(activationEvent, startup);
|
||||||
}
|
}
|
||||||
|
@ -885,6 +894,7 @@ export const IExtHostExtensionService = createDecorator<IExtHostExtensionService
|
||||||
export interface IExtHostExtensionService extends AbstractExtHostExtensionService {
|
export interface IExtHostExtensionService extends AbstractExtHostExtensionService {
|
||||||
readonly _serviceBrand: undefined;
|
readonly _serviceBrand: undefined;
|
||||||
initialize(): Promise<void>;
|
initialize(): Promise<void>;
|
||||||
|
getExtension(extensionId: string): Promise<IExtensionDescription | undefined>;
|
||||||
isActivated(extensionId: ExtensionIdentifier): boolean;
|
isActivated(extensionId: ExtensionIdentifier): boolean;
|
||||||
activateByIdWithErrors(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<void>;
|
activateByIdWithErrors(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<void>;
|
||||||
deactivateAll(): 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 { ExtensionStoragePaths } from 'vs/workbench/api/node/extHostStoragePaths';
|
||||||
import { ExtHostLoggerService } from 'vs/workbench/api/node/extHostLoggerService';
|
import { ExtHostLoggerService } from 'vs/workbench/api/node/extHostLoggerService';
|
||||||
import { ILoggerService } from 'vs/platform/log/common/log';
|
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(IExtHostTask, ExtHostTask);
|
||||||
registerSingleton(IExtHostTerminalService, ExtHostTerminalService);
|
registerSingleton(IExtHostTerminalService, ExtHostTerminalService);
|
||||||
registerSingleton(IExtHostTunnelService, ExtHostTunnelService);
|
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.
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import * as nls from 'vs/nls';
|
import { createCancelablePromise, firstParallel } from 'vs/base/common/async';
|
||||||
import type * as vscode from 'vscode';
|
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||||
import { homedir } from 'os';
|
|
||||||
import * as platform from 'vs/base/common/platform';
|
import * as platform from 'vs/base/common/platform';
|
||||||
import { DebugAdapterExecutable, ThemeIcon } from 'vs/workbench/api/common/extHostTypes';
|
import * as nls from 'vs/nls';
|
||||||
import { ExecutableDebugAdapter, SocketDebugAdapter, NamedPipeDebugAdapter } from 'vs/workbench/contrib/debug/node/debugAdapter';
|
import { IExternalTerminalService } from 'vs/platform/externalTerminal/common/externalTerminal';
|
||||||
import { AbstractDebugAdapter } from 'vs/workbench/contrib/debug/common/abstractDebugAdapter';
|
import { LinuxExternalTerminalService, MacExternalTerminalService, WindowsExternalTerminalService } from 'vs/platform/externalTerminal/node/externalTerminalService';
|
||||||
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 { ISignService } from 'vs/platform/sign/common/sign';
|
import { ISignService } from 'vs/platform/sign/common/sign';
|
||||||
import { SignService } from 'vs/platform/sign/node/signService';
|
import { SignService } from 'vs/platform/sign/node/signService';
|
||||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
import { ExtHostDebugServiceBase, ExtHostDebugSession } from 'vs/workbench/api/common/extHostDebugService';
|
||||||
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 { IExtHostEditorTabs } from 'vs/workbench/api/common/extHostEditorTabs';
|
import { IExtHostEditorTabs } from 'vs/workbench/api/common/extHostEditorTabs';
|
||||||
import { IExternalTerminalService } from 'vs/platform/externalTerminal/common/externalTerminal';
|
import { IExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensionService';
|
||||||
import { WindowsExternalTerminalService, MacExternalTerminalService, LinuxExternalTerminalService } from 'vs/platform/externalTerminal/node/externalTerminalService';
|
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 {
|
export class ExtHostDebugService extends ExtHostDebugServiceBase {
|
||||||
|
|
||||||
|
@ -40,12 +38,12 @@ export class ExtHostDebugService extends ExtHostDebugServiceBase {
|
||||||
@IExtHostRpcService extHostRpcService: IExtHostRpcService,
|
@IExtHostRpcService extHostRpcService: IExtHostRpcService,
|
||||||
@IExtHostWorkspace workspaceService: IExtHostWorkspace,
|
@IExtHostWorkspace workspaceService: IExtHostWorkspace,
|
||||||
@IExtHostExtensionService extensionService: IExtHostExtensionService,
|
@IExtHostExtensionService extensionService: IExtHostExtensionService,
|
||||||
@IExtHostDocumentsAndEditors editorsService: IExtHostDocumentsAndEditors,
|
|
||||||
@IExtHostConfiguration configurationService: IExtHostConfiguration,
|
@IExtHostConfiguration configurationService: IExtHostConfiguration,
|
||||||
@IExtHostTerminalService private _terminalService: IExtHostTerminalService,
|
@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 {
|
protected override createDebugAdapter(adapter: IAdapterDescriptor, session: ExtHostDebugSession): AbstractDebugAdapter | undefined {
|
||||||
|
@ -154,10 +152,6 @@ export class ExtHostDebugService extends ExtHostDebugServiceBase {
|
||||||
}
|
}
|
||||||
return super.$runInTerminal(args, sessionId);
|
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;
|
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 { IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
|
||||||
import type * as vscode from 'vscode';
|
import type * as vscode from 'vscode';
|
||||||
import * as tasks from '../common/shared/tasks';
|
import * as tasks from '../common/shared/tasks';
|
||||||
import { ExtHostVariableResolverService } from 'vs/workbench/api/common/extHostDebugService';
|
|
||||||
import { IExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
|
import { IExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
|
||||||
import { IExtHostConfiguration } from 'vs/workbench/api/common/extHostConfiguration';
|
import { IExtHostConfiguration } from 'vs/workbench/api/common/extHostConfiguration';
|
||||||
import { IWorkspaceFolder, WorkspaceFolder } from 'vs/platform/workspace/common/workspace';
|
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 { Schemas } from 'vs/base/common/network';
|
||||||
import { ILogService } from 'vs/platform/log/common/log';
|
import { ILogService } from 'vs/platform/log/common/log';
|
||||||
import { IExtHostApiDeprecationService } from 'vs/workbench/api/common/extHostApiDeprecationService';
|
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 * as resources from 'vs/base/common/resources';
|
||||||
import { homedir } from 'os';
|
import { homedir } from 'os';
|
||||||
|
import { IExtHostVariableResolverProvider } from 'vs/workbench/api/common/extHostVariableResolverService';
|
||||||
|
|
||||||
export class ExtHostTask extends ExtHostTaskBase {
|
export class ExtHostTask extends ExtHostTaskBase {
|
||||||
private _variableResolver: ExtHostVariableResolverService | undefined;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@IExtHostRpcService extHostRpc: IExtHostRpcService,
|
@IExtHostRpcService extHostRpc: IExtHostRpcService,
|
||||||
@IExtHostInitDataService initData: IExtHostInitDataService,
|
@IExtHostInitDataService initData: IExtHostInitDataService,
|
||||||
|
@ -39,7 +36,7 @@ export class ExtHostTask extends ExtHostTaskBase {
|
||||||
@IExtHostTerminalService extHostTerminalService: IExtHostTerminalService,
|
@IExtHostTerminalService extHostTerminalService: IExtHostTerminalService,
|
||||||
@ILogService logService: ILogService,
|
@ILogService logService: ILogService,
|
||||||
@IExtHostApiDeprecationService deprecationService: IExtHostApiDeprecationService,
|
@IExtHostApiDeprecationService deprecationService: IExtHostApiDeprecationService,
|
||||||
@IExtHostEditorTabs private readonly editorTabs: IExtHostEditorTabs,
|
@IExtHostVariableResolverProvider private readonly variableResolver: IExtHostVariableResolverProvider,
|
||||||
) {
|
) {
|
||||||
super(extHostRpc, initData, workspaceService, editorService, configurationService, extHostTerminalService, logService, deprecationService);
|
super(extHostRpc, initData, workspaceService, editorService, configurationService, extHostTerminalService, logService, deprecationService);
|
||||||
if (initData.remote.isRemote && initData.remote.authority) {
|
if (initData.remote.isRemote && initData.remote.authority) {
|
||||||
|
@ -128,14 +125,6 @@ export class ExtHostTask extends ExtHostTaskBase {
|
||||||
return resolvedTaskDTO;
|
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> {
|
private async getAFolder(workspaceFolders: vscode.WorkspaceFolder[] | undefined): Promise<IWorkspaceFolder> {
|
||||||
let folder = (workspaceFolders && workspaceFolders.length > 0) ? workspaceFolders[0] : undefined;
|
let folder = (workspaceFolders && workspaceFolders.length > 0) ? workspaceFolders[0] : undefined;
|
||||||
if (!folder) {
|
if (!folder) {
|
||||||
|
@ -161,7 +150,7 @@ export class ExtHostTask extends ExtHostTaskBase {
|
||||||
const workspaceFolder = await this._workspaceProvider.resolveWorkspaceFolder(uri);
|
const workspaceFolder = await this._workspaceProvider.resolveWorkspaceFolder(uri);
|
||||||
const workspaceFolders = (await this._workspaceProvider.getWorkspaceFolders2()) ?? [];
|
const workspaceFolders = (await this._workspaceProvider.getWorkspaceFolders2()) ?? [];
|
||||||
|
|
||||||
const resolver = await this.getVariableResolver(workspaceFolders);
|
const resolver = await this.variableResolver.getResolver();
|
||||||
const ws: IWorkspaceFolder = workspaceFolder ? {
|
const ws: IWorkspaceFolder = workspaceFolder ? {
|
||||||
uri: workspaceFolder.uri,
|
uri: workspaceFolder.uri,
|
||||||
name: workspaceFolder.name,
|
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);
|
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) {
|
if (!this._isDisposed && !shellLaunchConfig.strictEnv && !shellLaunchConfig.hideFromUser) {
|
||||||
this._extEnvironmentVariableCollection = this._environmentVariableService.mergedCollection;
|
this._extEnvironmentVariableCollection = this._environmentVariableService.mergedCollection;
|
||||||
this._register(this._environmentVariableService.onDidChangeCollections(newCollection => this._onEnvironmentVariableCollectionChange(newCollection)));
|
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
|
// 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
|
// 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.
|
// 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) {
|
if (this._extEnvironmentVariableCollection.map.size > 0) {
|
||||||
this.environmentVariableInfo = new EnvironmentVariableInfoChangesActive(this._extEnvironmentVariableCollection);
|
this.environmentVariableInfo = new EnvironmentVariableInfoChangesActive(this._extEnvironmentVariableCollection);
|
||||||
this._onEnvironmentVariableInfoChange.fire(this.environmentVariableInfo);
|
this._onEnvironmentVariableInfoChange.fire(this.environmentVariableInfo);
|
||||||
|
@ -423,7 +423,7 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce
|
||||||
|
|
||||||
const activeWorkspaceRootUri = this._historyService.getLastActiveWorkspaceRoot(Schemas.file);
|
const activeWorkspaceRootUri = this._historyService.getLastActiveWorkspaceRoot(Schemas.file);
|
||||||
|
|
||||||
const initialCwd = terminalEnvironment.getCwd(
|
const initialCwd = await terminalEnvironment.getCwd(
|
||||||
shellLaunchConfig,
|
shellLaunchConfig,
|
||||||
userHome,
|
userHome,
|
||||||
variableResolver,
|
variableResolver,
|
||||||
|
|
|
@ -355,25 +355,23 @@ export abstract class BaseTerminalProfileResolverService implements ITerminalPro
|
||||||
const env = await this._context.getEnvironment(options.remoteAuthority);
|
const env = await this._context.getEnvironment(options.remoteAuthority);
|
||||||
const activeWorkspaceRootUri = this._historyService.getLastActiveWorkspaceRoot(options.remoteAuthority ? Schemas.vscodeRemote : Schemas.file);
|
const activeWorkspaceRootUri = this._historyService.getLastActiveWorkspaceRoot(options.remoteAuthority ? Schemas.vscodeRemote : Schemas.file);
|
||||||
const lastActiveWorkspace = activeWorkspaceRootUri ? withNullAsUndefined(this._workspaceContextService.getWorkspaceFolder(activeWorkspaceRootUri)) : undefined;
|
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
|
// Resolve args variables
|
||||||
if (profile.args) {
|
if (profile.args) {
|
||||||
if (typeof profile.args === 'string') {
|
if (typeof profile.args === 'string') {
|
||||||
profile.args = this._resolveVariables(profile.args, env, lastActiveWorkspace);
|
profile.args = await this._resolveVariables(profile.args, env, lastActiveWorkspace);
|
||||||
} else {
|
} else {
|
||||||
for (let i = 0; i < profile.args.length; i++) {
|
profile.args = await Promise.all(profile.args.map(arg => this._resolveVariables(arg, env, lastActiveWorkspace)));
|
||||||
profile.args[i] = this._resolveVariables(profile.args[i], env, lastActiveWorkspace);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return profile;
|
return profile;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _resolveVariables(value: string, env: IProcessEnvironment, lastActiveWorkspace: IWorkspaceFolder | undefined) {
|
private async _resolveVariables(value: string, env: IProcessEnvironment, lastActiveWorkspace: IWorkspaceFolder | undefined) {
|
||||||
try {
|
try {
|
||||||
value = this._configurationResolverService.resolveWithEnvironment(env, lastActiveWorkspace, value);
|
value = await this._configurationResolverService.resolveWithEnvironment(env, lastActiveWorkspace, value);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this._logService.error(`Could not resolve shell`, 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 { Event } from 'vs/base/common/event';
|
||||||
import { IProcessEnvironment } from 'vs/base/common/platform';
|
import { IProcessEnvironment } from 'vs/base/common/platform';
|
||||||
import { ThemeIcon } from 'vs/platform/theme/common/themeService';
|
import { ThemeIcon } from 'vs/platform/theme/common/themeService';
|
||||||
|
import { VariableResolver } from 'vs/workbench/contrib/terminal/common/terminalEnvironment';
|
||||||
|
|
||||||
export const IEnvironmentVariableService = createDecorator<IEnvironmentVariableService>('environmentVariableService');
|
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
|
* @param variableResolver An optional function to use to resolve variables within the
|
||||||
* environment values.
|
* 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
|
* 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 { IProcessEnvironment, isWindows } from 'vs/base/common/platform';
|
||||||
import { EnvironmentVariableMutatorType, IEnvironmentVariableCollection, IExtensionOwnedEnvironmentVariableMutator, IMergedEnvironmentVariableCollection, IMergedEnvironmentVariableCollectionDiff } from 'vs/workbench/contrib/terminal/common/environmentVariable';
|
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 {
|
export class MergedEnvironmentVariableCollection implements IMergedEnvironmentVariableCollection {
|
||||||
readonly map: Map<string, IExtensionOwnedEnvironmentVariableMutator[]> = new Map();
|
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;
|
let lowerToActualVariableNames: { [lowerKey: string]: string | undefined } | undefined;
|
||||||
if (isWindows) {
|
if (isWindows) {
|
||||||
lowerToActualVariableNames = {};
|
lowerToActualVariableNames = {};
|
||||||
Object.keys(env).forEach(e => lowerToActualVariableNames![e.toLowerCase()] = e);
|
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;
|
const actualVariable = isWindows ? lowerToActualVariableNames![variable.toLowerCase()] || variable : variable;
|
||||||
mutators.forEach(mutator => {
|
for (const mutator of mutators) {
|
||||||
const value = variableResolver ? variableResolver(mutator.value) : mutator.value;
|
const value = variableResolver ? await variableResolver(mutator.value) : mutator.value;
|
||||||
switch (mutator.type) {
|
switch (mutator.type) {
|
||||||
case EnvironmentVariableMutatorType.Append:
|
case EnvironmentVariableMutatorType.Append:
|
||||||
env[actualVariable] = (env[actualVariable] || '') + value;
|
env[actualVariable] = (env[actualVariable] || '') + value;
|
||||||
|
@ -62,8 +63,8 @@ export class MergedEnvironmentVariableCollection implements IMergedEnvironmentVa
|
||||||
env[actualVariable] = value;
|
env[actualVariable] = value;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
diff(other: IMergedEnvironmentVariableCollection): IMergedEnvironmentVariableCollectionDiff | undefined {
|
diff(other: IMergedEnvironmentVariableCollection): IMergedEnvironmentVariableCollectionDiff | undefined {
|
||||||
|
|
|
@ -78,17 +78,17 @@ function mergeNonNullKeys(env: IProcessEnvironment, other: ITerminalEnvironment
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function resolveConfigurationVariables(variableResolver: VariableResolver, env: ITerminalEnvironment): ITerminalEnvironment {
|
async function resolveConfigurationVariables(variableResolver: VariableResolver, env: ITerminalEnvironment): Promise<ITerminalEnvironment> {
|
||||||
Object.keys(env).forEach((key) => {
|
await Promise.all(Object.entries(env).map(async ([key, value]) => {
|
||||||
const value = env[key];
|
|
||||||
if (typeof value === 'string') {
|
if (typeof value === 'string') {
|
||||||
try {
|
try {
|
||||||
env[key] = variableResolver(value);
|
env[key] = await variableResolver(value);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
env[key] = value;
|
env[key] = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
}));
|
||||||
|
|
||||||
return env;
|
return env;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -179,17 +179,17 @@ export function getLangEnvVariable(locale?: string): string {
|
||||||
return parts.join('_') + '.UTF-8';
|
return parts.join('_') + '.UTF-8';
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getCwd(
|
export async function getCwd(
|
||||||
shell: IShellLaunchConfig,
|
shell: IShellLaunchConfig,
|
||||||
userHome: string | undefined,
|
userHome: string | undefined,
|
||||||
variableResolver: VariableResolver | undefined,
|
variableResolver: VariableResolver | undefined,
|
||||||
root: Uri | undefined,
|
root: Uri | undefined,
|
||||||
customCwd: string | undefined,
|
customCwd: string | undefined,
|
||||||
logService?: ILogService
|
logService?: ILogService
|
||||||
): string {
|
): Promise<string> {
|
||||||
if (shell.cwd) {
|
if (shell.cwd) {
|
||||||
const unresolved = (typeof shell.cwd === 'object') ? shell.cwd.fsPath : 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);
|
return _sanitizeCwd(resolved || unresolved);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -197,7 +197,7 @@ export function getCwd(
|
||||||
|
|
||||||
if (!shell.ignoreConfigurationCwd && customCwd) {
|
if (!shell.ignoreConfigurationCwd && customCwd) {
|
||||||
if (variableResolver) {
|
if (variableResolver) {
|
||||||
customCwd = _resolveCwd(customCwd, variableResolver, logService);
|
customCwd = await _resolveCwd(customCwd, variableResolver, logService);
|
||||||
}
|
}
|
||||||
if (customCwd) {
|
if (customCwd) {
|
||||||
if (path.isAbsolute(customCwd)) {
|
if (path.isAbsolute(customCwd)) {
|
||||||
|
@ -216,10 +216,10 @@ export function getCwd(
|
||||||
return _sanitizeCwd(cwd);
|
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) {
|
if (variableResolver) {
|
||||||
try {
|
try {
|
||||||
return variableResolver(cwd);
|
return await variableResolver(cwd);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logService?.error('Could not resolve terminal cwd', e);
|
logService?.error('Could not resolve terminal cwd', e);
|
||||||
return undefined;
|
return undefined;
|
||||||
|
@ -251,7 +251,7 @@ export type TerminalShellArgsSetting = (
|
||||||
| TerminalSettingId.ShellArgsLinux
|
| 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 {
|
export function createVariableResolver(lastActiveWorkspace: IWorkspaceFolder | undefined, env: IProcessEnvironment, configurationResolverService: IConfigurationResolverService | undefined): VariableResolver | undefined {
|
||||||
if (!configurationResolverService) {
|
if (!configurationResolverService) {
|
||||||
|
@ -263,7 +263,7 @@ export function createVariableResolver(lastActiveWorkspace: IWorkspaceFolder | u
|
||||||
/**
|
/**
|
||||||
* @deprecated Use ITerminalProfileResolverService
|
* @deprecated Use ITerminalProfileResolverService
|
||||||
*/
|
*/
|
||||||
export function getDefaultShell(
|
export async function getDefaultShell(
|
||||||
fetchSetting: (key: TerminalShellSetting) => string | undefined,
|
fetchSetting: (key: TerminalShellSetting) => string | undefined,
|
||||||
defaultShell: string,
|
defaultShell: string,
|
||||||
isWoW64: boolean,
|
isWoW64: boolean,
|
||||||
|
@ -272,7 +272,7 @@ export function getDefaultShell(
|
||||||
logService: ILogService,
|
logService: ILogService,
|
||||||
useAutomationShell: boolean,
|
useAutomationShell: boolean,
|
||||||
platformOverride: Platform = platform
|
platformOverride: Platform = platform
|
||||||
): string {
|
): Promise<string> {
|
||||||
let maybeExecutable: string | undefined;
|
let maybeExecutable: string | undefined;
|
||||||
if (useAutomationShell) {
|
if (useAutomationShell) {
|
||||||
// If automationShell is specified, this should override the normal setting
|
// If automationShell is specified, this should override the normal setting
|
||||||
|
@ -300,7 +300,7 @@ export function getDefaultShell(
|
||||||
|
|
||||||
if (variableResolver) {
|
if (variableResolver) {
|
||||||
try {
|
try {
|
||||||
executable = variableResolver(executable);
|
executable = await variableResolver(executable);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logService.error(`Could not resolve shell`, e);
|
logService.error(`Could not resolve shell`, e);
|
||||||
}
|
}
|
||||||
|
@ -312,13 +312,13 @@ export function getDefaultShell(
|
||||||
/**
|
/**
|
||||||
* @deprecated Use ITerminalProfileResolverService
|
* @deprecated Use ITerminalProfileResolverService
|
||||||
*/
|
*/
|
||||||
export function getDefaultShellArgs(
|
export async function getDefaultShellArgs(
|
||||||
fetchSetting: (key: TerminalShellSetting | TerminalShellArgsSetting) => string | string[] | undefined,
|
fetchSetting: (key: TerminalShellSetting | TerminalShellArgsSetting) => string | string[] | undefined,
|
||||||
useAutomationShell: boolean,
|
useAutomationShell: boolean,
|
||||||
variableResolver: VariableResolver | undefined,
|
variableResolver: VariableResolver | undefined,
|
||||||
logService: ILogService,
|
logService: ILogService,
|
||||||
platformOverride: Platform = platform,
|
platformOverride: Platform = platform,
|
||||||
): string | string[] {
|
): Promise<string | string[]> {
|
||||||
if (useAutomationShell) {
|
if (useAutomationShell) {
|
||||||
if (!!getShellSetting(fetchSetting, 'automationShell', platformOverride)) {
|
if (!!getShellSetting(fetchSetting, 'automationShell', platformOverride)) {
|
||||||
return [];
|
return [];
|
||||||
|
@ -331,13 +331,13 @@ export function getDefaultShellArgs(
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
if (typeof args === 'string' && platformOverride === Platform.Windows) {
|
if (typeof args === 'string' && platformOverride === Platform.Windows) {
|
||||||
return variableResolver ? variableResolver(args) : args;
|
return variableResolver ? await variableResolver(args) : args;
|
||||||
}
|
}
|
||||||
if (variableResolver) {
|
if (variableResolver) {
|
||||||
const resolvedArgs: string[] = [];
|
const resolvedArgs: string[] = [];
|
||||||
for (const arg of args) {
|
for (const arg of args) {
|
||||||
try {
|
try {
|
||||||
resolvedArgs.push(variableResolver(arg));
|
resolvedArgs.push(await variableResolver(arg));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logService.error(`Could not resolve ${TerminalSettingPrefix.ShellArgs}${platformKey}`, e);
|
logService.error(`Could not resolve ${TerminalSettingPrefix.ShellArgs}${platformKey}`, e);
|
||||||
resolvedArgs.push(arg);
|
resolvedArgs.push(arg);
|
||||||
|
@ -357,14 +357,14 @@ function getShellSetting(
|
||||||
return fetchSetting(<TerminalShellSetting>`terminal.integrated.${type}.${platformKey}`);
|
return fetchSetting(<TerminalShellSetting>`terminal.integrated.${type}.${platformKey}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createTerminalEnvironment(
|
export async function createTerminalEnvironment(
|
||||||
shellLaunchConfig: IShellLaunchConfig,
|
shellLaunchConfig: IShellLaunchConfig,
|
||||||
envFromConfig: ITerminalEnvironment | undefined,
|
envFromConfig: ITerminalEnvironment | undefined,
|
||||||
variableResolver: VariableResolver | undefined,
|
variableResolver: VariableResolver | undefined,
|
||||||
version: string | undefined,
|
version: string | undefined,
|
||||||
detectLocale: 'auto' | 'off' | 'on',
|
detectLocale: 'auto' | 'off' | 'on',
|
||||||
baseEnv: IProcessEnvironment
|
baseEnv: IProcessEnvironment
|
||||||
): IProcessEnvironment {
|
): Promise<IProcessEnvironment> {
|
||||||
// Create a terminal environment based on settings, launch config and permissions
|
// Create a terminal environment based on settings, launch config and permissions
|
||||||
const env: IProcessEnvironment = {};
|
const env: IProcessEnvironment = {};
|
||||||
if (shellLaunchConfig.strictEnv) {
|
if (shellLaunchConfig.strictEnv) {
|
||||||
|
@ -379,10 +379,10 @@ export function createTerminalEnvironment(
|
||||||
// Resolve env vars from config and shell
|
// Resolve env vars from config and shell
|
||||||
if (variableResolver) {
|
if (variableResolver) {
|
||||||
if (allowedEnvFromConfig) {
|
if (allowedEnvFromConfig) {
|
||||||
resolveConfigurationVariables(variableResolver, allowedEnvFromConfig);
|
await resolveConfigurationVariables(variableResolver, allowedEnvFromConfig);
|
||||||
}
|
}
|
||||||
if (shellLaunchConfig.env) {
|
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 platformKey = isWindows ? 'windows' : (isMacintosh ? 'osx' : 'linux');
|
||||||
const envFromConfigValue = this._configurationService.getValue<ITerminalEnvironment | undefined>(`terminal.integrated.env.${platformKey}`);
|
const envFromConfigValue = this._configurationService.getValue<ITerminalEnvironment | undefined>(`terminal.integrated.env.${platformKey}`);
|
||||||
const baseEnv = await (shellLaunchConfig.useShellEnvironment ? this.getShellEnvironment() : this.getEnvironment());
|
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) {
|
if (!shellLaunchConfig.strictEnv && !shellLaunchConfig.hideFromUser) {
|
||||||
this._environmentVariableService.mergedCollection.applyToProcessEnvironment(env, variableResolver);
|
await this._environmentVariableService.mergedCollection.applyToProcessEnvironment(env, variableResolver);
|
||||||
}
|
}
|
||||||
return env;
|
return env;
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,7 +78,7 @@ suite('EnvironmentVariable - MergedEnvironmentVariableCollection', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
suite('applyToProcessEnvironment', () => {
|
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([
|
const merged = new MergedEnvironmentVariableCollection(new Map([
|
||||||
['ext', {
|
['ext', {
|
||||||
map: deserializeEnvironmentVariableCollection([
|
map: deserializeEnvironmentVariableCollection([
|
||||||
|
@ -93,7 +93,7 @@ suite('EnvironmentVariable - MergedEnvironmentVariableCollection', () => {
|
||||||
B: 'bar',
|
B: 'bar',
|
||||||
C: 'baz'
|
C: 'baz'
|
||||||
};
|
};
|
||||||
merged.applyToProcessEnvironment(env);
|
await merged.applyToProcessEnvironment(env);
|
||||||
deepStrictEqual(env, {
|
deepStrictEqual(env, {
|
||||||
A: 'a',
|
A: 'a',
|
||||||
B: 'barb',
|
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([
|
const merged = new MergedEnvironmentVariableCollection(new Map([
|
||||||
['ext', {
|
['ext', {
|
||||||
map: deserializeEnvironmentVariableCollection([
|
map: deserializeEnvironmentVariableCollection([
|
||||||
|
@ -112,7 +112,7 @@ suite('EnvironmentVariable - MergedEnvironmentVariableCollection', () => {
|
||||||
}]
|
}]
|
||||||
]));
|
]));
|
||||||
const env: IProcessEnvironment = {};
|
const env: IProcessEnvironment = {};
|
||||||
merged.applyToProcessEnvironment(env);
|
await merged.applyToProcessEnvironment(env);
|
||||||
deepStrictEqual(env, {
|
deepStrictEqual(env, {
|
||||||
A: 'a',
|
A: 'a',
|
||||||
B: 'b',
|
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([
|
const merged = new MergedEnvironmentVariableCollection(new Map([
|
||||||
['ext', {
|
['ext', {
|
||||||
map: deserializeEnvironmentVariableCollection([
|
map: deserializeEnvironmentVariableCollection([
|
||||||
|
@ -135,7 +135,7 @@ suite('EnvironmentVariable - MergedEnvironmentVariableCollection', () => {
|
||||||
B: 'B',
|
B: 'B',
|
||||||
C: 'C'
|
C: 'C'
|
||||||
};
|
};
|
||||||
merged.applyToProcessEnvironment(env);
|
await merged.applyToProcessEnvironment(env);
|
||||||
if (isWindows) {
|
if (isWindows) {
|
||||||
deepStrictEqual(env, {
|
deepStrictEqual(env, {
|
||||||
A: 'a',
|
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 collection1 = new Map<string, IEnvironmentVariableMutator>();
|
||||||
const collection2 = new Map<string, IEnvironmentVariableMutator>();
|
const collection2 = new Map<string, IEnvironmentVariableMutator>();
|
||||||
const collection3 = 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
|
// Verify the entries get applied to the environment as expected
|
||||||
const env: IProcessEnvironment = { A: 'foo' };
|
const env: IProcessEnvironment = { A: 'foo' };
|
||||||
environmentVariableService.mergedCollection.applyToProcessEnvironment(env);
|
await environmentVariableService.mergedCollection.applyToProcessEnvironment(env);
|
||||||
deepStrictEqual(env, { A: 'a2:a3:a1' });
|
deepStrictEqual(env, { A: 'a2:a3:a1' });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -180,66 +180,66 @@ suite('Workbench - TerminalEnvironment', () => {
|
||||||
strictEqual(Uri.file(a).fsPath, Uri.file(b).fsPath);
|
strictEqual(Uri.file(a).fsPath, Uri.file(b).fsPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
test('should default to userHome for an empty workspace', () => {
|
test('should default to userHome for an empty workspace', async () => {
|
||||||
assertPathsMatch(getCwd({ executable: undefined, args: [] }, '/userHome/', undefined, undefined, undefined), '/userHome/');
|
assertPathsMatch(await getCwd({ executable: undefined, args: [] }, '/userHome/', undefined, undefined, undefined), '/userHome/');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should use to the workspace if it exists', () => {
|
test('should use to the workspace if it exists', async () => {
|
||||||
assertPathsMatch(getCwd({ executable: undefined, args: [] }, '/userHome/', undefined, Uri.file('/foo'), undefined), '/foo');
|
assertPathsMatch(await getCwd({ executable: undefined, args: [] }, '/userHome/', undefined, Uri.file('/foo'), undefined), '/foo');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should use an absolute custom cwd as is', () => {
|
test('should use an absolute custom cwd as is', async () => {
|
||||||
assertPathsMatch(getCwd({ executable: undefined, args: [] }, '/userHome/', undefined, undefined, '/foo'), '/foo');
|
assertPathsMatch(await getCwd({ executable: undefined, args: [] }, '/userHome/', undefined, undefined, '/foo'), '/foo');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should normalize a relative custom cwd against the workspace path', () => {
|
test('should normalize a relative custom cwd against the workspace path', async () => {
|
||||||
assertPathsMatch(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(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(getCwd({ executable: undefined, args: [] }, '/userHome/', undefined, Uri.file('/bar'), '../foo'), '/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', () => {
|
test('should fall back for relative a custom cwd that doesn\'t have a workspace', async () => {
|
||||||
assertPathsMatch(getCwd({ executable: undefined, args: [] }, '/userHome/', undefined, undefined, 'foo'), '/userHome/');
|
assertPathsMatch(await getCwd({ executable: undefined, args: [] }, '/userHome/', undefined, undefined, 'foo'), '/userHome/');
|
||||||
assertPathsMatch(getCwd({ executable: undefined, args: [] }, '/userHome/', undefined, undefined, './foo'), '/userHome/');
|
assertPathsMatch(await getCwd({ executable: undefined, args: [] }, '/userHome/', undefined, undefined, './foo'), '/userHome/');
|
||||||
assertPathsMatch(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', () => {
|
test('should ignore custom cwd when told to ignore', async () => {
|
||||||
assertPathsMatch(getCwd({ executable: undefined, args: [], ignoreConfigurationCwd: true }, '/userHome/', undefined, Uri.file('/bar'), '/foo'), '/bar');
|
assertPathsMatch(await getCwd({ executable: undefined, args: [], ignoreConfigurationCwd: true }, '/userHome/', undefined, Uri.file('/bar'), '/foo'), '/bar');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
suite('getDefaultShell', () => {
|
suite('getDefaultShell', () => {
|
||||||
test('should change Sysnative to System32 in non-WoW64 systems', () => {
|
test('should change Sysnative to System32 in non-WoW64 systems', async () => {
|
||||||
const shell = getDefaultShell(key => {
|
const shell = await getDefaultShell(key => {
|
||||||
return ({ 'terminal.integrated.shell.windows': 'C:\\Windows\\Sysnative\\cmd.exe' } as any)[key];
|
return ({ 'terminal.integrated.shell.windows': 'C:\\Windows\\Sysnative\\cmd.exe' } as any)[key];
|
||||||
}, 'DEFAULT', false, 'C:\\Windows', undefined, {} as any, false, Platform.Windows);
|
}, 'DEFAULT', false, 'C:\\Windows', undefined, {} as any, false, Platform.Windows);
|
||||||
strictEqual(shell, 'C:\\Windows\\System32\\cmd.exe');
|
strictEqual(shell, 'C:\\Windows\\System32\\cmd.exe');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should not change Sysnative to System32 in WoW64 systems', () => {
|
test('should not change Sysnative to System32 in WoW64 systems', async () => {
|
||||||
const shell = getDefaultShell(key => {
|
const shell = await getDefaultShell(key => {
|
||||||
return ({ 'terminal.integrated.shell.windows': 'C:\\Windows\\Sysnative\\cmd.exe' } as any)[key];
|
return ({ 'terminal.integrated.shell.windows': 'C:\\Windows\\Sysnative\\cmd.exe' } as any)[key];
|
||||||
}, 'DEFAULT', true, 'C:\\Windows', undefined, {} as any, false, Platform.Windows);
|
}, 'DEFAULT', true, 'C:\\Windows', undefined, {} as any, false, Platform.Windows);
|
||||||
strictEqual(shell, 'C:\\Windows\\Sysnative\\cmd.exe');
|
strictEqual(shell, 'C:\\Windows\\Sysnative\\cmd.exe');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should use automationShell when specified', () => {
|
test('should use automationShell when specified', async () => {
|
||||||
const shell1 = getDefaultShell(key => {
|
const shell1 = await getDefaultShell(key => {
|
||||||
return ({
|
return ({
|
||||||
'terminal.integrated.shell.windows': 'shell',
|
'terminal.integrated.shell.windows': 'shell',
|
||||||
'terminal.integrated.automationShell.windows': undefined
|
'terminal.integrated.automationShell.windows': undefined
|
||||||
} as any)[key];
|
} as any)[key];
|
||||||
}, 'DEFAULT', false, 'C:\\Windows', undefined, {} as any, false, Platform.Windows);
|
}, 'DEFAULT', false, 'C:\\Windows', undefined, {} as any, false, Platform.Windows);
|
||||||
strictEqual(shell1, 'shell', 'automationShell was false');
|
strictEqual(shell1, 'shell', 'automationShell was false');
|
||||||
const shell2 = getDefaultShell(key => {
|
const shell2 = await getDefaultShell(key => {
|
||||||
return ({
|
return ({
|
||||||
'terminal.integrated.shell.windows': 'shell',
|
'terminal.integrated.shell.windows': 'shell',
|
||||||
'terminal.integrated.automationShell.windows': undefined
|
'terminal.integrated.automationShell.windows': undefined
|
||||||
} as any)[key];
|
} as any)[key];
|
||||||
}, 'DEFAULT', false, 'C:\\Windows', undefined, {} as any, true, Platform.Windows);
|
}, 'DEFAULT', false, 'C:\\Windows', undefined, {} as any, true, Platform.Windows);
|
||||||
strictEqual(shell2, 'shell', 'automationShell was true');
|
strictEqual(shell2, 'shell', 'automationShell was true');
|
||||||
const shell3 = getDefaultShell(key => {
|
const shell3 = await getDefaultShell(key => {
|
||||||
return ({
|
return ({
|
||||||
'terminal.integrated.shell.windows': 'shell',
|
'terminal.integrated.shell.windows': 'shell',
|
||||||
'terminal.integrated.automationShell.windows': 'automationShell'
|
'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 { IProcessEnvironment } from 'vs/base/common/platform';
|
||||||
import { ILabelService } from 'vs/platform/label/common/label';
|
import { ILabelService } from 'vs/platform/label/common/label';
|
||||||
import { IPathService } from 'vs/workbench/services/path/common/pathService';
|
import { IPathService } from 'vs/workbench/services/path/common/pathService';
|
||||||
|
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||||
|
|
||||||
export abstract class BaseConfigurationResolverService extends AbstractVariableResolverService {
|
export abstract class BaseConfigurationResolverService extends AbstractVariableResolverService {
|
||||||
|
|
||||||
|
@ -36,7 +37,8 @@ export abstract class BaseConfigurationResolverService extends AbstractVariableR
|
||||||
private readonly workspaceContextService: IWorkspaceContextService,
|
private readonly workspaceContextService: IWorkspaceContextService,
|
||||||
private readonly quickInputService: IQuickInputService,
|
private readonly quickInputService: IQuickInputService,
|
||||||
private readonly labelService: ILabelService,
|
private readonly labelService: ILabelService,
|
||||||
private readonly pathService: IPathService
|
private readonly pathService: IPathService,
|
||||||
|
extensionService: IExtensionService,
|
||||||
) {
|
) {
|
||||||
super({
|
super({
|
||||||
getFolderUri: (folderName: string): uri | undefined => {
|
getFolderUri: (folderName: string): uri | undefined => {
|
||||||
|
@ -109,7 +111,10 @@ export abstract class BaseConfigurationResolverService extends AbstractVariableR
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
},
|
||||||
|
getExtension: id => {
|
||||||
|
return extensionService.getExtension(id);
|
||||||
|
},
|
||||||
}, labelService, pathService.userHome().then(home => home.path), envVariablesPromise);
|
}, 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.
|
* 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 { ICommandService } from 'vs/platform/commands/common/commands';
|
||||||
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||||
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 { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
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 { 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 {
|
export class ConfigurationResolverService extends BaseConfigurationResolverService {
|
||||||
|
|
||||||
|
@ -23,11 +24,12 @@ export class ConfigurationResolverService extends BaseConfigurationResolverServi
|
||||||
@IWorkspaceContextService workspaceContextService: IWorkspaceContextService,
|
@IWorkspaceContextService workspaceContextService: IWorkspaceContextService,
|
||||||
@IQuickInputService quickInputService: IQuickInputService,
|
@IQuickInputService quickInputService: IQuickInputService,
|
||||||
@ILabelService labelService: ILabelService,
|
@ILabelService labelService: ILabelService,
|
||||||
@IPathService pathService: IPathService
|
@IPathService pathService: IPathService,
|
||||||
|
@IExtensionService extensionService: IExtensionService,
|
||||||
) {
|
) {
|
||||||
super({ getAppRoot: () => undefined, getExecPath: () => undefined },
|
super({ getAppRoot: () => undefined, getExecPath: () => undefined },
|
||||||
Promise.resolve(Object.create(null)), editorService, configurationService,
|
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 {
|
export interface IConfigurationResolverService {
|
||||||
readonly _serviceBrand: undefined;
|
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>;
|
||||||
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 { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver';
|
||||||
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
|
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
|
||||||
import { ILabelService } from 'vs/platform/label/common/label';
|
import { ILabelService } from 'vs/platform/label/common/label';
|
||||||
|
import { replaceAsync } from 'vs/base/common/strings';
|
||||||
|
|
||||||
export interface IVariableResolveContext {
|
export interface IVariableResolveContext {
|
||||||
getFolderUri(folderName: string): uri | undefined;
|
getFolderUri(folderName: string): uri | undefined;
|
||||||
|
@ -26,6 +27,7 @@ export interface IVariableResolveContext {
|
||||||
getWorkspaceFolderPathForFile?(): string | undefined;
|
getWorkspaceFolderPathForFile?(): string | undefined;
|
||||||
getSelectedText(): string | undefined;
|
getSelectedText(): string | undefined;
|
||||||
getLineNumber(): string | undefined;
|
getLineNumber(): string | undefined;
|
||||||
|
getExtension(id: string): Promise<{ readonly extensionLocation: uri } | undefined>;
|
||||||
}
|
}
|
||||||
|
|
||||||
type Environment = { env: IProcessEnvironment | undefined; userHome: string | undefined };
|
type Environment = { env: IProcessEnvironment | undefined; userHome: string | undefined };
|
||||||
|
@ -66,7 +68,7 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe
|
||||||
return envVariables;
|
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);
|
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)) {
|
if (types.isString(value)) {
|
||||||
return this.resolveString(environment, folderUri, value, commandValueMapping, resolvedVariables);
|
return this.resolveString(environment, folderUri, value, commandValueMapping, resolvedVariables);
|
||||||
} else if (types.isArray(value)) {
|
} 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)) {
|
} else if (types.isObject(value)) {
|
||||||
let result: IStringDictionary<string | IStringDictionary<string> | string[]> = Object.create(null);
|
let result: IStringDictionary<string | IStringDictionary<string> | string[]> = Object.create(null);
|
||||||
Object.keys(value).forEach(key => {
|
const replaced = await Promise.all(Object.keys(value).map(async key => {
|
||||||
const replaced = this.resolveString(environment, folderUri, key, commandValueMapping, resolvedVariables);
|
const replaced = await this.resolveString(environment, folderUri, key, commandValueMapping, resolvedVariables);
|
||||||
result[replaced] = this.recursiveResolve(environment, folderUri, value[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 result;
|
||||||
}
|
}
|
||||||
return value;
|
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'
|
// 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.
|
// disallow attempted nesting, see #77289. This doesn't exclude variables that resolve to other variables.
|
||||||
if (variable.includes(AbstractVariableResolverService.VARIABLE_LHS)) {
|
if (variable.includes(AbstractVariableResolverService.VARIABLE_LHS)) {
|
||||||
return match;
|
return match;
|
||||||
}
|
}
|
||||||
|
|
||||||
let resolvedValue = this.evaluateSingleVariable(environment, match, variable, folderUri, commandValueMapping);
|
let resolvedValue = await this.evaluateSingleVariable(environment, match, variable, folderUri, commandValueMapping);
|
||||||
|
|
||||||
if (resolvedVariables) {
|
if (resolvedVariables) {
|
||||||
resolvedVariables.set(variable, resolvedValue);
|
resolvedVariables.set(variable, resolvedValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((resolvedValue !== match) && types.isString(resolvedValue) && resolvedValue.match(AbstractVariableResolverService.VARIABLE_REGEXP)) {
|
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 resolvedValue;
|
||||||
});
|
});
|
||||||
|
|
||||||
return replaced;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fsPath(displayUri: uri): string {
|
private fsPath(displayUri: uri): string {
|
||||||
return this._labelService ? this._labelService.getUriLabel(displayUri, { noPrefix: true }) : displayUri.fsPath;
|
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
|
// try to separate variable arguments from variable name
|
||||||
let argument: string | undefined;
|
let argument: string | undefined;
|
||||||
|
@ -268,6 +271,16 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe
|
||||||
case 'input':
|
case 'input':
|
||||||
return this.resolveFromMap(match, argument, commandValueMapping, '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: {
|
default: {
|
||||||
|
|
||||||
switch (variable) {
|
switch (variable) {
|
||||||
|
|
|
@ -15,6 +15,7 @@ import { BaseConfigurationResolverService } from 'vs/workbench/services/configur
|
||||||
import { ILabelService } from 'vs/platform/label/common/label';
|
import { ILabelService } from 'vs/platform/label/common/label';
|
||||||
import { IShellEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/shellEnvironmentService';
|
import { IShellEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/shellEnvironmentService';
|
||||||
import { IPathService } from 'vs/workbench/services/path/common/pathService';
|
import { IPathService } from 'vs/workbench/services/path/common/pathService';
|
||||||
|
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||||
|
|
||||||
export class ConfigurationResolverService extends BaseConfigurationResolverService {
|
export class ConfigurationResolverService extends BaseConfigurationResolverService {
|
||||||
|
|
||||||
|
@ -27,7 +28,8 @@ export class ConfigurationResolverService extends BaseConfigurationResolverServi
|
||||||
@IQuickInputService quickInputService: IQuickInputService,
|
@IQuickInputService quickInputService: IQuickInputService,
|
||||||
@ILabelService labelService: ILabelService,
|
@ILabelService labelService: ILabelService,
|
||||||
@IShellEnvironmentService shellEnvironmentService: IShellEnvironmentService,
|
@IShellEnvironmentService shellEnvironmentService: IShellEnvironmentService,
|
||||||
@IPathService pathService: IPathService
|
@IPathService pathService: IPathService,
|
||||||
|
@IExtensionService extensionService: IExtensionService,
|
||||||
) {
|
) {
|
||||||
super({
|
super({
|
||||||
getAppRoot: (): string | undefined => {
|
getAppRoot: (): string | undefined => {
|
||||||
|
@ -35,9 +37,9 @@ export class ConfigurationResolverService extends BaseConfigurationResolverServi
|
||||||
},
|
},
|
||||||
getExecPath: (): string | undefined => {
|
getExecPath: (): string | undefined => {
|
||||||
return environmentService.execPath;
|
return environmentService.execPath;
|
||||||
}
|
},
|
||||||
}, shellEnvironmentService.getShellEnv(), editorService, configurationService, commandService,
|
}, shellEnvironmentService.getShellEnv(), editorService, configurationService, commandService,
|
||||||
workspaceContextService, quickInputService, labelService, pathService);
|
workspaceContextService, quickInputService, labelService, pathService, extensionService);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import * as assert from 'assert';
|
import * as assert from 'assert';
|
||||||
|
import { stub } from 'sinon';
|
||||||
import { Emitter, Event } from 'vs/base/common/event';
|
import { Emitter, Event } from 'vs/base/common/event';
|
||||||
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
|
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
|
||||||
import { Schemas } from 'vs/base/common/network';
|
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 { ICommandService } from 'vs/platform/commands/common/commands';
|
||||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||||
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
|
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 { IFormatterChangeEvent, ILabelService, ResourceLabelFormatter } from 'vs/platform/label/common/label';
|
||||||
import { IWorkspace, IWorkspaceFolder, IWorkspaceIdentifier, Workspace } from 'vs/platform/workspace/common/workspace';
|
import { IWorkspace, IWorkspaceFolder, IWorkspaceIdentifier, Workspace } from 'vs/platform/workspace/common/workspace';
|
||||||
import { testWorkspace } from 'vs/platform/workspace/test/common/testWorkspace';
|
import { testWorkspace } from 'vs/platform/workspace/test/common/testWorkspace';
|
||||||
import { BaseConfigurationResolverService } from 'vs/workbench/services/configurationResolver/browser/baseConfigurationResolverService';
|
import { BaseConfigurationResolverService } from 'vs/workbench/services/configurationResolver/browser/baseConfigurationResolverService';
|
||||||
import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver';
|
import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver';
|
||||||
import { NativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/environmentService';
|
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 { IPathService } from 'vs/workbench/services/path/common/pathService';
|
||||||
import { TestEditorService, TestQuickInputService } from 'vs/workbench/test/browser/workbenchTestServices';
|
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';
|
import { TestNativeWindowConfiguration } from 'vs/workbench/test/electron-browser/workbenchTestServices';
|
||||||
|
|
||||||
const mockLineNumber = 10;
|
const mockLineNumber = 10;
|
||||||
|
@ -68,6 +71,7 @@ suite('Configuration Resolver Service', () => {
|
||||||
let quickInputService: TestQuickInputService;
|
let quickInputService: TestQuickInputService;
|
||||||
let labelService: MockLabelService;
|
let labelService: MockLabelService;
|
||||||
let pathService: MockPathService;
|
let pathService: MockPathService;
|
||||||
|
let extensionService: IExtensionService;
|
||||||
|
|
||||||
setup(() => {
|
setup(() => {
|
||||||
mockCommandService = new MockCommandService();
|
mockCommandService = new MockCommandService();
|
||||||
|
@ -76,9 +80,10 @@ suite('Configuration Resolver Service', () => {
|
||||||
environmentService = new MockWorkbenchEnvironmentService(envVariables);
|
environmentService = new MockWorkbenchEnvironmentService(envVariables);
|
||||||
labelService = new MockLabelService();
|
labelService = new MockLabelService();
|
||||||
pathService = new MockPathService();
|
pathService = new MockPathService();
|
||||||
|
extensionService = new TestExtensionService();
|
||||||
containingWorkspace = testWorkspace(uri.parse('file:///VSCode/workspaceLocation'));
|
containingWorkspace = testWorkspace(uri.parse('file:///VSCode/workspaceLocation'));
|
||||||
workspace = containingWorkspace.folders[0];
|
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(() => {
|
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}}');
|
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', () => {
|
// test('substitute keys and values in object', () => {
|
||||||
// const myObject = {
|
// const myObject = {
|
||||||
// '${workspaceRootFolderName}': '${lineNumber}',
|
// '${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');
|
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');
|
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');
|
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) {
|
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');
|
assert.strictEqual(await service.resolveAsync(workspace, 'abc ${config:editor.fontFamily} ${workspaceFolder} ${env:key1} xyz'), 'abc foo \\VSCode\\workspaceLocation Value for key1 xyz');
|
||||||
} else {
|
} 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) {
|
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');
|
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 {
|
} 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');
|
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: {}
|
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 ${unknownVariable} xyz'), 'abc ${unknownVariable} xyz');
|
||||||
assert.strictEqual(await service.resolveAsync(workspace, 'abc ${env:unknownVariable} xyz'), 'abc 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'));
|
||||||
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 = {
|
const env = {
|
||||||
'VAR_1': 'VAL_1',
|
'VAR_1': 'VAL_1',
|
||||||
'VAR_2': 'VAL_2'
|
'VAR_2': 'VAL_2'
|
||||||
};
|
};
|
||||||
const configuration = 'echo ${env:VAR_1}${env:VAR_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');
|
assert.deepStrictEqual(resolvedResult, 'echo VAL_1VAL_2');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue