Skip to content

Commit c192454

Browse files
authored
Use global ZDOTDIR environment variable (microsoft#159783)
1 parent e42cb73 commit c192454

File tree

4 files changed

+61
-44
lines changed

4 files changed

+61
-44
lines changed

src/vs/platform/terminal/node/terminalEnvironment.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import { format } from 'vs/base/common/strings';
1313
import { isString } from 'vs/base/common/types';
1414
import * as pfs from 'vs/base/node/pfs';
1515
import { ILogService } from 'vs/platform/log/common/log';
16-
import { IShellLaunchConfig, ITerminalProcessOptions } from 'vs/platform/terminal/common/terminal';
16+
import { IShellLaunchConfig, ITerminalEnvironment, ITerminalProcessOptions } from 'vs/platform/terminal/common/terminal';
1717

1818
export function getWindowsBuildNumber(): number {
1919
const osVersion = (/(\d+)\.(\d+)\.(\d+)/g).exec(os.release());
@@ -104,6 +104,7 @@ export interface IShellIntegrationConfigInjection {
104104
export function getShellIntegrationInjection(
105105
shellLaunchConfig: IShellLaunchConfig,
106106
options: ITerminalProcessOptions['shellIntegration'],
107+
env: ITerminalEnvironment | undefined,
107108
logService: ILogService
108109
): IShellIntegrationConfigInjection | undefined {
109110
// Shell integration arg injection is disabled when:
@@ -186,6 +187,8 @@ export function getShellIntegrationInjection(
186187
// Move .zshrc into $ZDOTDIR as the way to activate the script
187188
const zdotdir = path.join(os.tmpdir(), `${os.userInfo().username}-vscode-zsh`);
188189
envMixin['ZDOTDIR'] = zdotdir;
190+
const userZdotdir = env?.ZDOTDIR ?? os.homedir();
191+
envMixin['USER_ZDOTDIR'] = userZdotdir;
189192
const filesToCopy: IShellIntegrationConfigInjection['filesToCopy'] = [];
190193
filesToCopy.push({
191194
source: path.join(appRoot, 'out/vs/workbench/contrib/terminal/browser/media/shellIntegration-rc.zsh'),

src/vs/platform/terminal/node/terminalProcess.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ export class TerminalProcess extends Disposable implements ITerminalChildProcess
201201

202202
let injection: IShellIntegrationConfigInjection | undefined;
203203
if (this._options.shellIntegration.enabled) {
204-
injection = getShellIntegrationInjection(this.shellLaunchConfig, this._options.shellIntegration, this._logService);
204+
injection = getShellIntegrationInjection(this.shellLaunchConfig, this._options.shellIntegration, this._ptyOptions.env, this._logService);
205205
if (injection) {
206206
this._onDidChangeProperty.fire({ type: ProcessPropertyType.UsedShellIntegrationInjection, value: true });
207207
if (injection.envMixin) {

src/vs/platform/terminal/test/node/terminalEnvironment.test.ts

Lines changed: 50 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
*--------------------------------------------------------------------------------------------*/
55

66
import { deepStrictEqual, ok, strictEqual } from 'assert';
7-
import { userInfo } from 'os';
7+
import { homedir, userInfo } from 'os';
88
import { NullLogService } from 'vs/platform/log/common/log';
99
import { ITerminalProcessOptions } from 'vs/platform/terminal/common/terminal';
1010
import { getShellIntegrationInjection, IShellIntegrationConfigInjection } from 'vs/platform/terminal/node/terminalEnvironment';
@@ -14,13 +14,14 @@ const disabledProcessOptions: ITerminalProcessOptions['shellIntegration'] = { en
1414
const pwshExe = process.platform === 'win32' ? 'pwsh.exe' : 'pwsh';
1515
const repoRoot = process.platform === 'win32' ? process.cwd()[0].toLowerCase() + process.cwd().substring(1) : process.cwd();
1616
const logService = new NullLogService();
17+
const defaultEnvironment = {};
1718

1819
suite('platform - terminalEnvironment', () => {
1920
suite('getShellIntegrationInjection', () => {
2021
suite('should not enable', () => {
2122
test('when isFeatureTerminal or when no executable is provided', () => {
22-
ok(!getShellIntegrationInjection({ executable: pwshExe, args: ['-l', '-NoLogo'], isFeatureTerminal: true }, enabledProcessOptions, logService));
23-
ok(getShellIntegrationInjection({ executable: pwshExe, args: ['-l', '-NoLogo'], isFeatureTerminal: false }, enabledProcessOptions, logService));
23+
ok(!getShellIntegrationInjection({ executable: pwshExe, args: ['-l', '-NoLogo'], isFeatureTerminal: true }, enabledProcessOptions, defaultEnvironment, logService));
24+
ok(getShellIntegrationInjection({ executable: pwshExe, args: ['-l', '-NoLogo'], isFeatureTerminal: false }, enabledProcessOptions, defaultEnvironment, logService));
2425
});
2526
});
2627

@@ -40,21 +41,21 @@ suite('platform - terminalEnvironment', () => {
4041
}
4142
});
4243
test('when undefined, []', () => {
43-
deepStrictEqual(getShellIntegrationInjection({ executable: pwshExe, args: [] }, enabledProcessOptions, logService), enabledExpectedResult);
44-
deepStrictEqual(getShellIntegrationInjection({ executable: pwshExe, args: undefined }, enabledProcessOptions, logService), enabledExpectedResult);
44+
deepStrictEqual(getShellIntegrationInjection({ executable: pwshExe, args: [] }, enabledProcessOptions, defaultEnvironment, logService), enabledExpectedResult);
45+
deepStrictEqual(getShellIntegrationInjection({ executable: pwshExe, args: undefined }, enabledProcessOptions, defaultEnvironment, logService), enabledExpectedResult);
4546
});
4647
suite('when no logo', () => {
4748
test('array - case insensitive', () => {
48-
deepStrictEqual(getShellIntegrationInjection({ executable: pwshExe, args: ['-NoLogo'] }, enabledProcessOptions, logService), enabledExpectedResult);
49-
deepStrictEqual(getShellIntegrationInjection({ executable: pwshExe, args: ['-NOLOGO'] }, enabledProcessOptions, logService), enabledExpectedResult);
50-
deepStrictEqual(getShellIntegrationInjection({ executable: pwshExe, args: ['-nol'] }, enabledProcessOptions, logService), enabledExpectedResult);
51-
deepStrictEqual(getShellIntegrationInjection({ executable: pwshExe, args: ['-NOL'] }, enabledProcessOptions, logService), enabledExpectedResult);
49+
deepStrictEqual(getShellIntegrationInjection({ executable: pwshExe, args: ['-NoLogo'] }, enabledProcessOptions, defaultEnvironment, logService), enabledExpectedResult);
50+
deepStrictEqual(getShellIntegrationInjection({ executable: pwshExe, args: ['-NOLOGO'] }, enabledProcessOptions, defaultEnvironment, logService), enabledExpectedResult);
51+
deepStrictEqual(getShellIntegrationInjection({ executable: pwshExe, args: ['-nol'] }, enabledProcessOptions, defaultEnvironment, logService), enabledExpectedResult);
52+
deepStrictEqual(getShellIntegrationInjection({ executable: pwshExe, args: ['-NOL'] }, enabledProcessOptions, defaultEnvironment, logService), enabledExpectedResult);
5253
});
5354
test('string - case insensitive', () => {
54-
deepStrictEqual(getShellIntegrationInjection({ executable: pwshExe, args: '-NoLogo' }, enabledProcessOptions, logService), enabledExpectedResult);
55-
deepStrictEqual(getShellIntegrationInjection({ executable: pwshExe, args: '-NOLOGO' }, enabledProcessOptions, logService), enabledExpectedResult);
56-
deepStrictEqual(getShellIntegrationInjection({ executable: pwshExe, args: '-nol' }, enabledProcessOptions, logService), enabledExpectedResult);
57-
deepStrictEqual(getShellIntegrationInjection({ executable: pwshExe, args: '-NOL' }, enabledProcessOptions, logService), enabledExpectedResult);
55+
deepStrictEqual(getShellIntegrationInjection({ executable: pwshExe, args: '-NoLogo' }, enabledProcessOptions, defaultEnvironment, logService), enabledExpectedResult);
56+
deepStrictEqual(getShellIntegrationInjection({ executable: pwshExe, args: '-NOLOGO' }, enabledProcessOptions, defaultEnvironment, logService), enabledExpectedResult);
57+
deepStrictEqual(getShellIntegrationInjection({ executable: pwshExe, args: '-nol' }, enabledProcessOptions, defaultEnvironment, logService), enabledExpectedResult);
58+
deepStrictEqual(getShellIntegrationInjection({ executable: pwshExe, args: '-NOL' }, enabledProcessOptions, defaultEnvironment, logService), enabledExpectedResult);
5859
});
5960
});
6061
});
@@ -71,23 +72,23 @@ suite('platform - terminalEnvironment', () => {
7172
}
7273
});
7374
test('when array contains no logo and login', () => {
74-
deepStrictEqual(getShellIntegrationInjection({ executable: pwshExe, args: ['-l', '-NoLogo'] }, enabledProcessOptions, logService), enabledExpectedResult);
75+
deepStrictEqual(getShellIntegrationInjection({ executable: pwshExe, args: ['-l', '-NoLogo'] }, enabledProcessOptions, defaultEnvironment, logService), enabledExpectedResult);
7576
});
7677
test('when string', () => {
77-
deepStrictEqual(getShellIntegrationInjection({ executable: pwshExe, args: '-l' }, enabledProcessOptions, logService), enabledExpectedResult);
78+
deepStrictEqual(getShellIntegrationInjection({ executable: pwshExe, args: '-l' }, enabledProcessOptions, defaultEnvironment, logService), enabledExpectedResult);
7879
});
7980
});
8081
suite('should not modify args', () => {
8182
test('when shell integration is disabled', () => {
82-
strictEqual(getShellIntegrationInjection({ executable: pwshExe, args: ['-l'] }, disabledProcessOptions, logService), undefined);
83-
strictEqual(getShellIntegrationInjection({ executable: pwshExe, args: '-l' }, disabledProcessOptions, logService), undefined);
84-
strictEqual(getShellIntegrationInjection({ executable: pwshExe, args: undefined }, disabledProcessOptions, logService), undefined);
83+
strictEqual(getShellIntegrationInjection({ executable: pwshExe, args: ['-l'] }, disabledProcessOptions, defaultEnvironment, logService), undefined);
84+
strictEqual(getShellIntegrationInjection({ executable: pwshExe, args: '-l' }, disabledProcessOptions, defaultEnvironment, logService), undefined);
85+
strictEqual(getShellIntegrationInjection({ executable: pwshExe, args: undefined }, disabledProcessOptions, defaultEnvironment, logService), undefined);
8586
});
8687
test('when using unrecognized arg', () => {
87-
strictEqual(getShellIntegrationInjection({ executable: pwshExe, args: ['-l', '-NoLogo', '-i'] }, disabledProcessOptions, logService), undefined);
88+
strictEqual(getShellIntegrationInjection({ executable: pwshExe, args: ['-l', '-NoLogo', '-i'] }, disabledProcessOptions, defaultEnvironment, logService), undefined);
8889
});
8990
test('when using unrecognized arg (string)', () => {
90-
strictEqual(getShellIntegrationInjection({ executable: pwshExe, args: '-i' }, disabledProcessOptions, logService), undefined);
91+
strictEqual(getShellIntegrationInjection({ executable: pwshExe, args: '-i' }, disabledProcessOptions, defaultEnvironment, logService), undefined);
9192
});
9293
});
9394
});
@@ -97,6 +98,7 @@ suite('platform - terminalEnvironment', () => {
9798
suite('should override args', () => {
9899
const username = userInfo().username;
99100
const expectedDir = new RegExp(`.+\/${username}-vscode-zsh`);
101+
const customZdotdir = '/custom/zsh/dotdir';
100102
const expectedDests = [
101103
new RegExp(`.+\/${username}-vscode-zsh\/\.zshrc`),
102104
new RegExp(`.+\/${username}-vscode-zsh\/\.zprofile`),
@@ -109,9 +111,10 @@ suite('platform - terminalEnvironment', () => {
109111
/.+\/out\/vs\/workbench\/contrib\/terminal\/browser\/media\/shellIntegration-env.zsh/,
110112
/.+\/out\/vs\/workbench\/contrib\/terminal\/browser\/media\/shellIntegration-login.zsh/
111113
];
112-
function assertIsEnabled(result: IShellIntegrationConfigInjection) {
113-
strictEqual(Object.keys(result.envMixin!).length, 2);
114+
function assertIsEnabled(result: IShellIntegrationConfigInjection, globalZdotdir = homedir()) {
115+
strictEqual(Object.keys(result.envMixin!).length, 3);
114116
ok(result.envMixin!['ZDOTDIR']?.match(expectedDir));
117+
strictEqual(result.envMixin!['USER_ZDOTDIR'], globalZdotdir);
115118
ok(result.envMixin!['VSCODE_INJECTION']?.match('1'));
116119
strictEqual(result.filesToCopy?.length, 4);
117120
ok(result.filesToCopy[0].dest.match(expectedDests[0]));
@@ -124,27 +127,39 @@ suite('platform - terminalEnvironment', () => {
124127
ok(result.filesToCopy[3].source.match(expectedSources[3]));
125128
}
126129
test('when undefined, []', () => {
127-
const result1 = getShellIntegrationInjection({ executable: 'zsh', args: [] }, enabledProcessOptions, logService);
130+
const result1 = getShellIntegrationInjection({ executable: 'zsh', args: [] }, enabledProcessOptions, defaultEnvironment, logService);
128131
deepStrictEqual(result1?.newArgs, ['-i']);
129132
assertIsEnabled(result1);
130-
const result2 = getShellIntegrationInjection({ executable: 'zsh', args: undefined }, enabledProcessOptions, logService);
133+
const result2 = getShellIntegrationInjection({ executable: 'zsh', args: undefined }, enabledProcessOptions, defaultEnvironment, logService);
131134
deepStrictEqual(result2?.newArgs, ['-i']);
132135
assertIsEnabled(result2);
133136
});
134137
suite('should incorporate login arg', () => {
135138
test('when array', () => {
136-
const result = getShellIntegrationInjection({ executable: 'zsh', args: ['-l'] }, enabledProcessOptions, logService);
139+
const result = getShellIntegrationInjection({ executable: 'zsh', args: ['-l'] }, enabledProcessOptions, defaultEnvironment, logService);
137140
deepStrictEqual(result?.newArgs, ['-il']);
138141
assertIsEnabled(result);
139142
});
140143
});
141144
suite('should not modify args', () => {
142145
test('when shell integration is disabled', () => {
143-
strictEqual(getShellIntegrationInjection({ executable: 'zsh', args: ['-l'] }, disabledProcessOptions, logService), undefined);
144-
strictEqual(getShellIntegrationInjection({ executable: 'zsh', args: undefined }, disabledProcessOptions, logService), undefined);
146+
strictEqual(getShellIntegrationInjection({ executable: 'zsh', args: ['-l'] }, disabledProcessOptions, defaultEnvironment, logService), undefined);
147+
strictEqual(getShellIntegrationInjection({ executable: 'zsh', args: undefined }, disabledProcessOptions, defaultEnvironment, logService), undefined);
145148
});
146149
test('when using unrecognized arg', () => {
147-
strictEqual(getShellIntegrationInjection({ executable: 'zsh', args: ['-l', '-fake'] }, disabledProcessOptions, logService), undefined);
150+
strictEqual(getShellIntegrationInjection({ executable: 'zsh', args: ['-l', '-fake'] }, disabledProcessOptions, defaultEnvironment, logService), undefined);
151+
});
152+
});
153+
suite('should incorporate global ZDOTDIR env variable', () => {
154+
test('when custom ZDOTDIR', () => {
155+
const result1 = getShellIntegrationInjection({ executable: 'zsh', args: [] }, enabledProcessOptions, { ...defaultEnvironment, ZDOTDIR: customZdotdir }, logService);
156+
deepStrictEqual(result1?.newArgs, ['-i']);
157+
assertIsEnabled(result1, customZdotdir);
158+
});
159+
test('when undefined', () => {
160+
const result1 = getShellIntegrationInjection({ executable: 'zsh', args: [] }, enabledProcessOptions, undefined, logService);
161+
deepStrictEqual(result1?.newArgs, ['-i']);
162+
assertIsEnabled(result1);
148163
});
149164
});
150165
});
@@ -161,9 +176,9 @@ suite('platform - terminalEnvironment', () => {
161176
VSCODE_INJECTION: '1'
162177
}
163178
});
164-
deepStrictEqual(getShellIntegrationInjection({ executable: 'bash', args: [] }, enabledProcessOptions, logService), enabledExpectedResult);
165-
deepStrictEqual(getShellIntegrationInjection({ executable: 'bash', args: '' }, enabledProcessOptions, logService), enabledExpectedResult);
166-
deepStrictEqual(getShellIntegrationInjection({ executable: 'bash', args: undefined }, enabledProcessOptions, logService), enabledExpectedResult);
179+
deepStrictEqual(getShellIntegrationInjection({ executable: 'bash', args: [] }, enabledProcessOptions, defaultEnvironment, logService), enabledExpectedResult);
180+
deepStrictEqual(getShellIntegrationInjection({ executable: 'bash', args: '' }, enabledProcessOptions, defaultEnvironment, logService), enabledExpectedResult);
181+
deepStrictEqual(getShellIntegrationInjection({ executable: 'bash', args: undefined }, enabledProcessOptions, defaultEnvironment, logService), enabledExpectedResult);
167182
});
168183
suite('should set login env variable and not modify args', () => {
169184
const enabledExpectedResult = Object.freeze<IShellIntegrationConfigInjection>({
@@ -177,16 +192,16 @@ suite('platform - terminalEnvironment', () => {
177192
}
178193
});
179194
test('when array', () => {
180-
deepStrictEqual(getShellIntegrationInjection({ executable: 'bash', args: ['-l'] }, enabledProcessOptions, logService), enabledExpectedResult);
195+
deepStrictEqual(getShellIntegrationInjection({ executable: 'bash', args: ['-l'] }, enabledProcessOptions, defaultEnvironment, logService), enabledExpectedResult);
181196
});
182197
});
183198
suite('should not modify args', () => {
184199
test('when shell integration is disabled', () => {
185-
strictEqual(getShellIntegrationInjection({ executable: 'bash', args: ['-l'] }, disabledProcessOptions, logService), undefined);
186-
strictEqual(getShellIntegrationInjection({ executable: 'bash', args: undefined }, disabledProcessOptions, logService), undefined);
200+
strictEqual(getShellIntegrationInjection({ executable: 'bash', args: ['-l'] }, disabledProcessOptions, defaultEnvironment, logService), undefined);
201+
strictEqual(getShellIntegrationInjection({ executable: 'bash', args: undefined }, disabledProcessOptions, defaultEnvironment, logService), undefined);
187202
});
188203
test('when custom array entry', () => {
189-
strictEqual(getShellIntegrationInjection({ executable: 'bash', args: ['-l', '-i'] }, disabledProcessOptions, logService), undefined);
204+
strictEqual(getShellIntegrationInjection({ executable: 'bash', args: ['-l', '-i'] }, disabledProcessOptions, defaultEnvironment, logService), undefined);
190205
});
191206
});
192207
});

src/vs/workbench/contrib/terminal/browser/media/shellIntegration-env.zsh

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,12 @@
22
# Copyright (c) Microsoft Corporation. All rights reserved.
33
# Licensed under the MIT License. See License.txt in the project root for license information.
44
# ---------------------------------------------------------------------------------------------
5-
VSCODE_ZDOTDIR=$ZDOTDIR
6-
if [[ -f ~/.zshenv ]]; then
7-
. ~/.zshenv
8-
fi
9-
if [[ "$ZDOTDIR" != "$VSCODE_ZDOTDIR" ]]; then
5+
if [[ -f $USER_ZDOTDIR/.zshenv ]]; then
6+
VSCODE_ZDOTDIR=$ZDOTDIR
7+
ZDOTDIR=$USER_ZDOTDIR
8+
9+
. $USER_ZDOTDIR/.zshenv
10+
1011
USER_ZDOTDIR=$ZDOTDIR
1112
ZDOTDIR=$VSCODE_ZDOTDIR
12-
else
13-
USER_ZDOTDIR=$HOME
1413
fi

0 commit comments

Comments
 (0)