diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/terminal.shellIntegration.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/terminal.shellIntegration.test.ts index db48c2593e073..726f0ed278f37 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/terminal.shellIntegration.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/terminal.shellIntegration.test.ts @@ -87,6 +87,16 @@ import { assertNoRpc } from '../utils'; await closeTerminalAsync(terminal); }); + if (platform() === 'darwin' || platform() === 'linux') { + test('Test if env is set', async () => { + const { shellIntegration } = await createTerminalAndWaitForShellIntegration(); + const env = shellIntegration.env; + ok(env); + ok(env.PATH); + ok(env.PATH.length > 0, 'env.PATH should have a length greater than 0'); + }); + } + test('execution events should fire in order when a command runs', async () => { const { terminal, shellIntegration } = await createTerminalAndWaitForShellIntegration(); const events: string[] = []; diff --git a/src/vs/platform/terminal/common/capabilities/capabilities.ts b/src/vs/platform/terminal/common/capabilities/capabilities.ts index 50f39332a4316..ba7597b9d926d 100644 --- a/src/vs/platform/terminal/common/capabilities/capabilities.ts +++ b/src/vs/platform/terminal/common/capabilities/capabilities.ts @@ -155,6 +155,9 @@ export interface IShellEnvDetectionCapability { readonly onDidChangeEnv: Event>; get env(): Map; setEnvironment(envs: { [key: string]: string | undefined } | undefined, isTrusted: boolean): void; + startEnvironmentSingleVar(isTrusted: boolean): void; + setEnvironmentSingleVar(key: string, value: string | undefined, isTrusted: boolean): void; + endEnvironmentSingleVar(isTrusted: boolean): void; } export const enum CommandInvalidationReason { diff --git a/src/vs/platform/terminal/common/capabilities/shellEnvDetectionCapability.ts b/src/vs/platform/terminal/common/capabilities/shellEnvDetectionCapability.ts index 95e1d827dc434..be9577acfe939 100644 --- a/src/vs/platform/terminal/common/capabilities/shellEnvDetectionCapability.ts +++ b/src/vs/platform/terminal/common/capabilities/shellEnvDetectionCapability.ts @@ -11,7 +11,8 @@ import { equals } from '../../../../base/common/objects.js'; export class ShellEnvDetectionCapability extends Disposable implements IShellEnvDetectionCapability { readonly type = TerminalCapability.ShellEnvDetection; - private readonly _env: Map = new Map(); + private _pendingEnv: Map | undefined; + private _env: Map = new Map(); get env(): Map { return this._env; } private readonly _onDidChangeEnv = this._register(new Emitter>()); @@ -36,4 +37,30 @@ export class ShellEnvDetectionCapability extends Disposable implements IShellEnv // Convert to event and fire event this._onDidChangeEnv.fire(this._env); } + + startEnvironmentSingleVar(isTrusted: boolean): void { + if (!isTrusted) { + return; + } + this._pendingEnv = new Map(); + } + setEnvironmentSingleVar(key: string, value: string | undefined, isTrusted: boolean): void { + if (!isTrusted) { + return; + } + if (key !== undefined && value !== undefined) { + this._pendingEnv?.set(key, value); + } + } + endEnvironmentSingleVar(isTrusted: boolean): void { + if (!isTrusted) { + return; + } + if (!this._pendingEnv) { + return; + } + this._env = this._pendingEnv; + this._pendingEnv = undefined; + this._onDidChangeEnv.fire(this._env); + } } diff --git a/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts b/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts index ee7dbff267e52..92b1851c275c8 100644 --- a/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts +++ b/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts @@ -239,6 +239,34 @@ const enum VSCodeOscPt { */ EnvJson = 'EnvJson', + /** + * The start of the collecting user's environment variables individually. + * Clears any environment residuals in previous sessions. + * + * Format: `OSC 633 ; EnvSingleStart ; ` + * + * WARNING: This sequence is unfinalized, DO NOT use this in your shell integration script. + */ + EnvSingleStart = 'EnvSingleStart', + + /** + * Sets an entry of single environment variable to transactional pending map of environment variables. + * + * Format: `OSC 633 ; EnvSingleEntry ; ; ; ` + * + * WARNING: This sequence is unfinalized, DO NOT use this in your shell integration script. + */ + EnvSingleEntry = 'EnvSingleEntry', + + /** + * The end of the collecting user's environment variables individually. + * Clears any pending environment variables and fires an event that contains user's environment. + * + * Format: `OSC 633 ; EnvSingleEnd ; ` + * + * WARNING: This sequence is unfinalized, DO NOT use this in your shell integration script. + */ + EnvSingleEnd = 'EnvSingleEnd' } /** @@ -446,6 +474,24 @@ export class ShellIntegrationAddon extends Disposable implements IShellIntegrati } return true; } + case VSCodeOscPt.EnvSingleStart: { + this._createOrGetShellEnvDetection().startEnvironmentSingleVar(args[0] === this._nonce); + return true; + } + case VSCodeOscPt.EnvSingleEntry: { + const arg0 = args[0]; + const arg1 = args[1]; + const arg2 = args[2]; + if (arg0 !== undefined && arg1 !== undefined) { + const env = deserializeMessage(arg1); + this._createOrGetShellEnvDetection().setEnvironmentSingleVar(arg0, env, arg2 === this._nonce); + } + return true; + } + case VSCodeOscPt.EnvSingleEnd: { + this._createOrGetShellEnvDetection().endEnvironmentSingleVar(args[0] === this._nonce); + return true; + } case VSCodeOscPt.RightPromptStart: { this._createOrGetCommandDetection(this._terminal).handleRightPromptStart(); return true; diff --git a/src/vs/workbench/contrib/terminal/common/scripts/shellIntegration-bash.sh b/src/vs/workbench/contrib/terminal/common/scripts/shellIntegration-bash.sh index fbeaa22f90a7f..3040617a5a6de 100644 --- a/src/vs/workbench/contrib/terminal/common/scripts/shellIntegration-bash.sh +++ b/src/vs/workbench/contrib/terminal/common/scripts/shellIntegration-bash.sh @@ -214,6 +214,17 @@ __vsc_update_cwd() { builtin printf '\e]633;P;Cwd=%s\a' "$(__vsc_escape_value "$__vsc_cwd")" } +__vsc_update_env() { + builtin printf '\e]633;EnvSingleStart;%s;\a' $__vsc_nonce + for var in $(compgen -v); do + if printenv "$var" >/dev/null 2>&1; then + value=$(builtin printf '%s' "${!var}") + builtin printf '\e]633;EnvSingleEntry;%s;%s;%s\a' "$var" "$(__vsc_escape_value "$value")" $__vsc_nonce + fi + done + builtin printf '\e]633;EnvSingleEnd;%s;\a' $__vsc_nonce +} + __vsc_command_output_start() { if [[ -z "${__vsc_first_prompt-}" ]]; then builtin return @@ -240,6 +251,10 @@ __vsc_command_complete() { builtin printf '\e]633;D;%s\a' "$__vsc_status" fi __vsc_update_cwd + + if [ "$__vsc_stable" = "0" ]; then + __vsc_update_env + fi } __vsc_update_prompt() { # in command execution @@ -269,6 +284,10 @@ __vsc_precmd() { fi __vsc_first_prompt=1 __vsc_update_prompt + + if [ "$__vsc_stable" = "0" ]; then + __vsc_update_env + fi } __vsc_preexec() {