diff --git a/src/vs/workbench/api/node/extHostDebugService.ts b/src/vs/workbench/api/node/extHostDebugService.ts index 995d4f8a17b3..5e50ef5c47e7 100644 --- a/src/vs/workbench/api/node/extHostDebugService.ts +++ b/src/vs/workbench/api/node/extHostDebugService.ts @@ -27,7 +27,6 @@ import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/c import type * as vscode from 'vscode'; import { ExtHostConfigProvider, IExtHostConfiguration } from '../common/extHostConfiguration'; import { IExtHostCommands } from 'vs/workbench/api/common/extHostCommands'; -import { createHash } from 'crypto'; export class ExtHostDebugService extends ExtHostDebugServiceBase { @@ -90,8 +89,8 @@ export class ExtHostDebugService extends ExtHostDebugServiceBase { const terminalName = args.title || nls.localize('debug.terminal.title', "Debug Process"); - const termKey = createKeyForShell(shell, shellArgs, args); - let terminal = await this._integratedTerminalInstances.checkout(termKey, terminalName, true); + const shellConfig = JSON.stringify({ shell, shellArgs }); + let terminal = await this._integratedTerminalInstances.checkout(shellConfig, terminalName); let cwdForPrepareCommand: string | undefined; let giveShellTimeToInitialize = false; @@ -103,7 +102,6 @@ export class ExtHostDebugService extends ExtHostDebugServiceBase { cwd: args.cwd, name: terminalName, iconPath: new ThemeIcon('debug'), - env: args.env, }; giveShellTimeToInitialize = true; terminal = this._terminalService.createTerminalFromOptions(options, { @@ -113,7 +111,7 @@ export class ExtHostDebugService extends ExtHostDebugServiceBase { forceShellIntegration: true, useShellEnvironment: true }); - this._integratedTerminalInstances.insert(terminal, termKey); + this._integratedTerminalInstances.insert(terminal, shellConfig); } else { cwdForPrepareCommand = args.cwd; @@ -145,7 +143,7 @@ export class ExtHostDebugService extends ExtHostDebugServiceBase { } } - const command = prepareCommand(shell, args.args, !!args.argsCanBeInterpretedByShell, cwdForPrepareCommand); + const command = prepareCommand(shell, args.args, !!args.argsCanBeInterpretedByShell, cwdForPrepareCommand, args.env); terminal.sendText(command); // Mark terminal as unused when its session ends, see #112055 @@ -165,14 +163,6 @@ export class ExtHostDebugService extends ExtHostDebugServiceBase { } } -/** Creates a key that determines how terminals get reused */ -function createKeyForShell(shell: string, shellArgs: string | string[], args: DebugProtocol.RunInTerminalRequestArguments) { - const hash = createHash('sha256'); - hash.update(JSON.stringify({ shell, shellArgs })); - hash.update(JSON.stringify(Object.entries(args.env || {}).sort(([k1], [k2]) => k1.localeCompare(k2)))); - return hash.digest('base64'); -} - let externalTerminalService: IExternalTerminalService | undefined = undefined; function runInExternalTerminal(args: DebugProtocol.RunInTerminalRequestArguments, configProvider: ExtHostConfigProvider): Promise { diff --git a/src/vs/workbench/contrib/debug/node/terminals.ts b/src/vs/workbench/contrib/debug/node/terminals.ts index 84c3d7947d41..c3f3cd929283 100644 --- a/src/vs/workbench/contrib/debug/node/terminals.ts +++ b/src/vs/workbench/contrib/debug/node/terminals.ts @@ -56,7 +56,7 @@ export async function hasChildProcesses(processId: number | undefined): Promise< const enum ShellType { cmd, powershell, bash } -export function prepareCommand(shell: string, args: string[], argsCanBeInterpretedByShell: boolean, cwd?: string): string { +export function prepareCommand(shell: string, args: string[], argsCanBeInterpretedByShell: boolean, cwd?: string, env?: { [key: string]: string | null }): string { shell = shell.trim().toLowerCase(); @@ -97,6 +97,16 @@ export function prepareCommand(shell: string, args: string[], argsCanBeInterpret } command += `cd ${quote(cwd)}; `; } + if (env) { + for (const key in env) { + const value = env[key]; + if (value === null) { + command += `Remove-Item env:${key}; `; + } else { + command += `\${env:${key}}='${value}'; `; + } + } + } if (args.length > 0) { const arg = args.shift()!; const cmd = argsCanBeInterpretedByShell ? arg : quote(arg); @@ -127,10 +137,25 @@ export function prepareCommand(shell: string, args: string[], argsCanBeInterpret } command += `cd ${quote(cwd)} && `; } + if (env) { + command += 'cmd /C "'; + for (const key in env) { + let value = env[key]; + if (value === null) { + command += `set "${key}=" && `; + } else { + value = value.replace(/[&^|<>]/g, s => `^${s}`); + command += `set "${key}=${value}" && `; + } + } + } for (const a of args) { command += (a === '<' || a === '>' || argsCanBeInterpretedByShell) ? a : quote(a); command += ' '; } + if (env) { + command += '"'; + } break; case ShellType.bash: { @@ -140,9 +165,25 @@ export function prepareCommand(shell: string, args: string[], argsCanBeInterpret return s.length === 0 ? `""` : s; }; + const hardQuote = (s: string) => { + return /[^\w@%\/+=,.:^-]/.test(s) ? `'${s.replace(/'/g, '\'\\\'\'')}'` : s; + }; + if (cwd) { command += `cd ${quote(cwd)} ; `; } + if (env) { + command += '/usr/bin/env'; + for (const key in env) { + const value = env[key]; + if (value === null) { + command += ` -u ${hardQuote(key)}`; + } else { + command += ` ${hardQuote(`${key}=${value}`)}`; + } + } + command += ' '; + } for (const a of args) { command += (a === '<' || a === '>' || argsCanBeInterpretedByShell) ? a : quote(a); command += ' ';