diff --git a/build/filters.js b/build/filters.js index d0aa87355c3..87ded512d05 100644 --- a/build/filters.js +++ b/build/filters.js @@ -141,6 +141,7 @@ module.exports.copyrightFilter = [ '!**/*.icns', '!**/*.xml', '!**/*.sh', + '!**/*.zsh', '!**/*.txt', '!**/*.xpm', '!**/*.opts', diff --git a/build/gulpfile.reh.js b/build/gulpfile.reh.js index b695a105ee4..e99a1a1a306 100644 --- a/build/gulpfile.reh.js +++ b/build/gulpfile.reh.js @@ -75,9 +75,9 @@ const serverResources = [ 'out-build/vs/base/node/ps.sh', // Terminal shell integration - 'out-build/vs/workbench/contrib/terminal/browser/media/shellIntegration-bash.sh', - 'out-build/vs/workbench/contrib/terminal/browser/media/.zshrc', 'out-build/vs/workbench/contrib/terminal/browser/media/shellIntegration.ps1', + 'out-build/vs/workbench/contrib/terminal/browser/media/shellIntegration-bash.sh', + 'out-build/vs/workbench/contrib/terminal/browser/media/shellIntegration.zsh', '!**/test/**' ]; diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index 80b1cce4332..2f5a931e850 100644 --- a/build/gulpfile.vscode.js +++ b/build/gulpfile.vscode.js @@ -71,8 +71,8 @@ const vscodeResources = [ 'out-build/vs/workbench/contrib/debug/**/*.json', 'out-build/vs/workbench/contrib/externalTerminal/**/*.scpt', 'out-build/vs/workbench/contrib/terminal/browser/media/*.ps1', - 'out-build/vs/workbench/contrib/terminal/browser/media/.zshrc', 'out-build/vs/workbench/contrib/terminal/browser/media/*.sh', + 'out-build/vs/workbench/contrib/terminal/browser/media/*.zsh', 'out-build/vs/workbench/contrib/webview/browser/pre/*.js', 'out-build/vs/**/markdown.css', 'out-build/vs/workbench/contrib/tasks/**/*.json', diff --git a/src/vs/platform/terminal/node/terminalProcess.ts b/src/vs/platform/terminal/node/terminalProcess.ts index b30e5c07374..71db32abfce 100644 --- a/src/vs/platform/terminal/node/terminalProcess.ts +++ b/src/vs/platform/terminal/node/terminalProcess.ts @@ -4,10 +4,13 @@ *--------------------------------------------------------------------------------------------*/ import { exec } from 'child_process'; +import { promises as fs } from 'fs'; import type * as pty from 'node-pty'; +import { tmpdir } from 'os'; import { timeout } from 'vs/base/common/async'; import { Emitter, Event } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; +import { FileAccess } from 'vs/base/common/network'; import * as path from 'vs/base/common/path'; import { IProcessEnvironment, isLinux, isMacintosh, isWindows } from 'vs/base/common/platform'; import { URI } from 'vs/base/common/uri'; @@ -184,6 +187,17 @@ export class TerminalProcess extends Disposable implements ITerminalChildProcess return firstError; } + // Handle zsh shell integration - Set $ZDOTDIR to a temp dir and create $ZDOTDIR/.zshrc + if (this.shellLaunchConfig.env?.['_ZDOTDIR'] === '1') { + const zdotdir = path.join(tmpdir(), 'vscode-zsh'); + await fs.mkdir(zdotdir, { recursive: true }); + const source = path.join(path.dirname(FileAccess.asFileUri('', require).fsPath), 'out/vs/workbench/contrib/terminal/browser/media/shellIntegration.zsh'); + await fs.copyFile(source, path.join(zdotdir, '.zshrc')); + this._ptyOptions.env = this._ptyOptions.env || {}; + this._ptyOptions.env['ZDOTDIR'] = zdotdir; + delete this._ptyOptions.env['_ZDOTDIR']; + } + try { await this.setupPtyProcess(this.shellLaunchConfig, this._ptyOptions); return undefined; diff --git a/src/vs/workbench/contrib/terminal/browser/media/.zshrc b/src/vs/workbench/contrib/terminal/browser/media/shellIntegration.zsh old mode 100755 new mode 100644 similarity index 100% rename from src/vs/workbench/contrib/terminal/browser/media/.zshrc rename to src/vs/workbench/contrib/terminal/browser/media/shellIntegration.zsh diff --git a/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts index f35509b463d..258957ada24 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts @@ -273,9 +273,9 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce shellLaunchConfig.env = shellLaunchConfig.env || {} as IProcessEnvironment; shellLaunchConfig.env['VSCODE_SHELL_LOGIN'] = '1'; } - if (env?.['ZDOTDIR']) { + if (env?.['_ZDOTDIR']) { shellLaunchConfig.env = shellLaunchConfig.env || {} as IProcessEnvironment; - shellLaunchConfig.env['ZDOTDIR'] = env['ZDOTDIR'].replace('${execInstallFolder}', remoteEnv.appRoot.fsPath); + shellLaunchConfig.env['_ZDOTDIR'] = '1'; } newProcess = await backend.createProcess( @@ -455,12 +455,9 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce const shellIntegration = terminalEnvironment.injectShellIntegrationArgs(this._logService, this._configurationService, env, this._configHelper.config.shellIntegration?.enabled || false, shellLaunchConfig, OS); if (shellIntegration.enableShellIntegration) { shellLaunchConfig.args = shellIntegration.args; - if (env?.['ZDOTDIR']) { + if (env?.['_ZDOTDIR']) { shellLaunchConfig.env = shellLaunchConfig.env || {} as IProcessEnvironment; - const activeWorkspaceRootUri = this._historyService.getLastActiveWorkspaceRoot(Schemas.file); - const lastActiveWorkspaceRoot = activeWorkspaceRootUri ? withNullAsUndefined(this._workspaceContextService.getWorkspaceFolder(activeWorkspaceRootUri)) : undefined; - const resolved = await this._configurationResolverService.resolveAsync(lastActiveWorkspaceRoot, env['ZDOTDIR']); - env['ZDOTDIR'] = resolved; + shellLaunchConfig.env['_ZDOTDIR'] = '1'; } // Always resolve the injected arguments on local processes await this._terminalProfileResolverService.resolveShellLaunchConfig(shellLaunchConfig, { diff --git a/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts b/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts index 96210ec7965..bbddbae1f31 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts @@ -494,9 +494,8 @@ export function injectShellIntegrationArgs( } else if (originalArgs === shellIntegrationArgs.get(ShellIntegrationExecutable.Zsh) || originalArgs === shellIntegrationArgs.get(ShellIntegrationExecutable.ZshLogin)) { newArgs = originalArgs; } - // Set the ZDOTDIR to be the dir of the shell integration script so that it runs - // as a .zshrc file and the autoload hook will work and set precmd and preexec correctly - env['ZDOTDIR'] = '${execInstallFolder}/out/vs/workbench/contrib/terminal/browser/media'; + // Set _ZDOTDIR which will defer setting ZDOTDIR to the pty host + env['_ZDOTDIR'] = '1'; const showWelcome = configurationService.getValue(TerminalSettingId.ShellIntegrationShowWelcome); if (!showWelcome) { env['VSCODE_SHELL_HIDE_WELCOME'] = '1';