Skip to content

Commit e68c9e3

Browse files
committed
chore: refactoring startup script update
1 parent b387ade commit e68c9e3

File tree

9 files changed

+142
-32
lines changed

9 files changed

+142
-32
lines changed

src/extension.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,11 @@ import { registerCompletionProvider } from './features/settings/settingCompletio
4848
import { setActivateMenuButtonContext } from './features/terminal/activateMenuButton';
4949
import { ShellStartupActivationManagerImpl } from './features/terminal/activateUsingShellStartup';
5050
import { normalizeShellPath } from './features/terminal/shells/common/shellUtils';
51-
import { createShellEnvProviders, createShellStartupProviders } from './features/terminal/shells/providers';
51+
import {
52+
clearShellProfileCache,
53+
createShellEnvProviders,
54+
createShellStartupProviders,
55+
} from './features/terminal/shells/providers';
5256
import { TerminalActivationImpl } from './features/terminal/terminalActivationState';
5357
import { TerminalManager, TerminalManagerImpl } from './features/terminal/terminalManager';
5458
import { getEnvironmentForTerminal } from './features/terminal/utils';
@@ -94,7 +98,11 @@ export async function activate(context: ExtensionContext): Promise<PythonEnviron
9498
shellEnvsProviders,
9599
envManagers,
96100
);
97-
const terminalManager: TerminalManager = new TerminalManagerImpl(terminalActivation, shellEnvsProviders);
101+
const terminalManager: TerminalManager = new TerminalManagerImpl(
102+
terminalActivation,
103+
shellEnvsProviders,
104+
shellStartupProviders,
105+
);
98106
context.subscriptions.push(terminalActivation, terminalManager, shellStartupActivationManager);
99107

100108
const projectCreators: ProjectCreators = new ProjectCreatorsImpl();
@@ -185,6 +193,7 @@ export async function activate(context: ExtensionContext): Promise<PythonEnviron
185193
}),
186194
commands.registerCommand('python-envs.clearCache', async () => {
187195
await envManagers.clearCache(undefined);
196+
await clearShellProfileCache(shellStartupProviders);
188197
}),
189198
commands.registerCommand('python-envs.runInTerminal', (item) => {
190199
return runInTerminalCommand(item, api, terminalManager);

src/features/terminal/activateUsingShellStartup.ts

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { ConfigurationChangeEvent, Disposable, GlobalEnvironmentVariableCollection } from 'vscode';
22
import { DidChangeEnvironmentEventArgs } from '../../api';
33
import { ShellStartupActivationStrings } from '../../common/localize';
4-
import { traceError, traceInfo } from '../../common/logging';
4+
import { traceInfo } from '../../common/logging';
55
import { showErrorMessage, showInformationMessage } from '../../common/window.apis';
66
import { getWorkspaceFolder, getWorkspaceFolders, onDidChangeConfiguration } from '../../common/workspace.apis';
77
import { EnvironmentManagers } from '../../internal.api';
@@ -90,28 +90,30 @@ export class ShellStartupActivationManagerImpl implements ShellStartupActivation
9090
return providers;
9191
}
9292

93+
private async check() {
94+
const providers = await this.getSetupRequired();
95+
if (providers.length > 0) {
96+
const shells = providers.map((provider) => provider.name).join(', ');
97+
const result = await showInformationMessage(
98+
ShellStartupActivationStrings.shellStartupScriptEditPrompt,
99+
{ modal: true, detail: `${ShellStartupActivationStrings.updatingTheseProfiles}: ${shells}` },
100+
ShellStartupActivationStrings.updateScript,
101+
);
102+
103+
if (ShellStartupActivationStrings.updateScript === result) {
104+
await this.updateStartupScripts();
105+
} else {
106+
traceError('User declined to edit shell startup scripts. See <doc-link> for more information.');
107+
traceInfo('Setting `python-envs.terminal.autoActivationType` to `command`.');
108+
setAutoActivationType('command');
109+
return;
110+
}
111+
}
112+
}
113+
93114
public async initialize(): Promise<void> {
94115
const autoActType = getAutoActivationType();
95116
if (autoActType === 'shellStartup') {
96-
const providers = await this.getSetupRequired();
97-
if (providers.length > 0) {
98-
const shells = providers.map((provider) => provider.name).join(', ');
99-
const result = await showInformationMessage(
100-
ShellStartupActivationStrings.shellStartupScriptEditPrompt,
101-
{ modal: true, detail: `${ShellStartupActivationStrings.updatingTheseProfiles}: ${shells}` },
102-
ShellStartupActivationStrings.updateScript,
103-
);
104-
105-
if (ShellStartupActivationStrings.updateScript === result) {
106-
await this.updateStartupScripts();
107-
} else {
108-
traceError('User declined to edit shell startup scripts. See <doc-link> for more information.');
109-
traceInfo('Setting `python-envs.terminal.autoActivationType` to `command`.');
110-
setAutoActivationType('command');
111-
return;
112-
}
113-
}
114-
115117
const workspaces = getWorkspaceFolders() ?? [];
116118

117119
if (workspaces.length > 0) {

src/features/terminal/shells/bash/bashStartup.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,10 @@ export class BashStartupProvider implements ShellStartupScriptProvider {
171171
return ShellScriptEditState.NotEdited;
172172
}
173173
}
174+
175+
clearCache(): Promise<void> {
176+
return Promise.resolve();
177+
}
174178
}
175179

176180
export class ZshStartupProvider implements ShellStartupScriptProvider {
@@ -229,6 +233,9 @@ export class ZshStartupProvider implements ShellStartupScriptProvider {
229233
return ShellScriptEditState.NotEdited;
230234
}
231235
}
236+
clearCache(): Promise<void> {
237+
return Promise.resolve();
238+
}
232239
}
233240

234241
export class GitBashStartupProvider implements ShellStartupScriptProvider {
@@ -286,4 +293,7 @@ export class GitBashStartupProvider implements ShellStartupScriptProvider {
286293
return ShellScriptEditState.NotEdited;
287294
}
288295
}
296+
clearCache(): Promise<void> {
297+
return Promise.resolve();
298+
}
289299
}

src/features/terminal/shells/cmd/cmdStartup.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,4 +309,8 @@ export class CmdStartupProvider implements ShellStartupScriptProvider {
309309
return ShellScriptEditState.NotEdited;
310310
}
311311
}
312+
313+
clearCache(): Promise<void> {
314+
return Promise.resolve();
315+
}
312316
}

src/features/terminal/shells/fish/fishStartup.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,4 +148,8 @@ export class FishStartupProvider implements ShellStartupScriptProvider {
148148
return ShellScriptEditState.NotEdited;
149149
}
150150
}
151+
152+
clearCache(): Promise<void> {
153+
return Promise.resolve();
154+
}
151155
}

src/features/terminal/shells/providers.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,7 @@ export function createShellEnvProviders(): ShellEnvsProvider[] {
3333
new ZshEnvsProvider(),
3434
];
3535
}
36+
37+
export async function clearShellProfileCache(providers: ShellStartupScriptProvider[]): Promise<void> {
38+
await Promise.all(providers.map((provider) => provider.clearCache()));
39+
}

src/features/terminal/shells/pwsh/pwshStartup.ts

Lines changed: 70 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,46 @@ import { isWindows } from '../../../../common/utils/platformUtils';
77
import { ShellScriptEditState, ShellSetupState, ShellStartupScriptProvider } from '../startupProvider';
88
import { runCommand } from '../utils';
99

10+
import { getGlobalPersistentState } from '../../../../common/persistentState';
1011
import { ShellConstants } from '../../../common/shellConstants';
1112
import { hasStartupCode, insertStartupCode, removeStartupCode } from '../common/editUtils';
1213
import { extractProfilePath, PROFILE_TAG_END, PROFILE_TAG_START } from '../common/shellUtils';
1314
import { POWERSHELL_ENV_KEY, PWSH_SCRIPT_VERSION } from './pwshConstants';
1415

16+
const PWSH_PROFILE_PATH_CACHE_KEY = 'PWSH_PROFILE_PATH_CACHE';
17+
const PS5_PROFILE_PATH_CACHE_KEY = 'PS5_PROFILE_PATH_CACHE';
18+
let pwshProfilePath: string | undefined;
19+
let ps5ProfilePath: string | undefined;
20+
async function clearPwshCache(shell: 'powershell' | 'pwsh'): Promise<void> {
21+
const global = await getGlobalPersistentState();
22+
if (shell === 'powershell') {
23+
ps5ProfilePath = undefined;
24+
await global.clear([PS5_PROFILE_PATH_CACHE_KEY]);
25+
} else {
26+
pwshProfilePath = undefined;
27+
await global.clear([PWSH_PROFILE_PATH_CACHE_KEY]);
28+
}
29+
}
30+
31+
async function setProfilePathCache(shell: 'powershell' | 'pwsh', profilePath: string): Promise<void> {
32+
const global = await getGlobalPersistentState();
33+
if (shell === 'powershell') {
34+
ps5ProfilePath = profilePath;
35+
await global.set(PS5_PROFILE_PATH_CACHE_KEY, profilePath);
36+
} else {
37+
pwshProfilePath = profilePath;
38+
await global.set(PWSH_PROFILE_PATH_CACHE_KEY, profilePath);
39+
}
40+
}
41+
42+
function getProfilePathCache(shell: 'powershell' | 'pwsh'): string | undefined {
43+
if (shell === 'powershell') {
44+
return ps5ProfilePath;
45+
} else {
46+
return pwshProfilePath;
47+
}
48+
}
49+
1550
async function isPowerShellInstalled(shell: string): Promise<boolean> {
1651
try {
1752
await which(shell);
@@ -23,6 +58,12 @@ async function isPowerShellInstalled(shell: string): Promise<boolean> {
2358
}
2459

2560
async function getProfileForShell(shell: 'powershell' | 'pwsh'): Promise<string> {
61+
const cachedPath = getProfilePathCache(shell);
62+
if (cachedPath) {
63+
traceInfo(`SHELL: ${shell} profile path from cache: ${cachedPath}`);
64+
return cachedPath;
65+
}
66+
2667
try {
2768
const content = await runCommand(
2869
isWindows()
@@ -33,6 +74,7 @@ async function getProfileForShell(shell: 'powershell' | 'pwsh'): Promise<string>
3374
if (content) {
3475
const profilePath = extractProfilePath(content);
3576
if (profilePath) {
77+
setProfilePathCache(shell, profilePath);
3678
traceInfo(`SHELL: ${shell} profile found at: ${profilePath}`);
3779
return profilePath;
3880
}
@@ -146,9 +188,16 @@ async function removePowerShellStartup(shell: string, profile: string): Promise<
146188
export class PowerShellClassicStartupProvider implements ShellStartupScriptProvider {
147189
public readonly name: string = 'PowerShell5';
148190
public readonly shellType: string = 'powershell';
191+
private _isInstalled: boolean | undefined;
149192

193+
private async checkInstallation(): Promise<boolean> {
194+
if (this._isInstalled === undefined) {
195+
this._isInstalled = await isPowerShellInstalled('powershell');
196+
}
197+
return this._isInstalled;
198+
}
150199
async isSetup(): Promise<ShellSetupState> {
151-
const isInstalled = await isPowerShellInstalled('powershell');
200+
const isInstalled = await this.checkInstallation();
152201
if (!isInstalled) {
153202
traceVerbose('PowerShell is not installed');
154203
return ShellSetupState.NotInstalled;
@@ -165,7 +214,7 @@ export class PowerShellClassicStartupProvider implements ShellStartupScriptProvi
165214
}
166215

167216
async setupScripts(): Promise<ShellScriptEditState> {
168-
const isInstalled = await isPowerShellInstalled('powershell');
217+
const isInstalled = await this.checkInstallation();
169218
if (!isInstalled) {
170219
traceVerbose('PowerShell is not installed');
171220
return ShellScriptEditState.NotInstalled;
@@ -182,7 +231,7 @@ export class PowerShellClassicStartupProvider implements ShellStartupScriptProvi
182231
}
183232

184233
async teardownScripts(): Promise<ShellScriptEditState> {
185-
const isInstalled = await isPowerShellInstalled('powershell');
234+
const isInstalled = await this.checkInstallation();
186235
if (!isInstalled) {
187236
traceVerbose('PowerShell is not installed');
188237
return ShellScriptEditState.NotInstalled;
@@ -197,14 +246,26 @@ export class PowerShellClassicStartupProvider implements ShellStartupScriptProvi
197246
}
198247
return ShellScriptEditState.NotEdited;
199248
}
249+
async clearCache(): Promise<void> {
250+
await clearPwshCache('powershell');
251+
}
200252
}
201253

202254
export class PwshStartupProvider implements ShellStartupScriptProvider {
203255
public readonly name: string = 'PowerShell';
204256
public readonly shellType: string = ShellConstants.PWSH;
205257

258+
private _isInstalled: boolean | undefined;
259+
260+
private async checkInstallation(): Promise<boolean> {
261+
if (this._isInstalled === undefined) {
262+
this._isInstalled = await isPowerShellInstalled('pwsh');
263+
}
264+
return this._isInstalled;
265+
}
266+
206267
async isSetup(): Promise<ShellSetupState> {
207-
const isInstalled = await isPowerShellInstalled('pwsh');
268+
const isInstalled = await this.checkInstallation();
208269
if (!isInstalled) {
209270
traceVerbose('PowerShell is not installed');
210271
return ShellSetupState.NotInstalled;
@@ -221,7 +282,7 @@ export class PwshStartupProvider implements ShellStartupScriptProvider {
221282
}
222283

223284
async setupScripts(): Promise<ShellScriptEditState> {
224-
const isInstalled = await isPowerShellInstalled('pwsh');
285+
const isInstalled = await this.checkInstallation();
225286
if (!isInstalled) {
226287
traceVerbose('PowerShell is not installed');
227288
return ShellScriptEditState.NotInstalled;
@@ -238,7 +299,7 @@ export class PwshStartupProvider implements ShellStartupScriptProvider {
238299
}
239300

240301
async teardownScripts(): Promise<ShellScriptEditState> {
241-
const isInstalled = await isPowerShellInstalled('pwsh');
302+
const isInstalled = await this.checkInstallation();
242303
if (!isInstalled) {
243304
traceVerbose('PowerShell is not installed');
244305
return ShellScriptEditState.NotInstalled;
@@ -253,4 +314,7 @@ export class PwshStartupProvider implements ShellStartupScriptProvider {
253314
}
254315
return ShellScriptEditState.NotEdited;
255316
}
317+
async clearCache(): Promise<void> {
318+
await clearPwshCache('pwsh');
319+
}
256320
}

src/features/terminal/shells/startupProvider.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export interface ShellStartupScriptProvider {
1919
isSetup(): Promise<ShellSetupState>;
2020
setupScripts(): Promise<ShellScriptEditState>;
2121
teardownScripts(): Promise<ShellScriptEditState>;
22+
clearCache(): Promise<void>;
2223
}
2324

2425
export interface ShellEnvsProvider {

src/features/terminal/terminalManager.ts

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import { getConfiguration } from '../../common/workspace.apis';
1414
import { isActivatableEnvironment } from '../common/activation';
1515
import { identifyTerminalShell } from '../common/shellDetector';
1616
import { getPythonApi } from '../pythonApi';
17-
import { ShellEnvsProvider } from './shells/startupProvider';
17+
import { ShellEnvsProvider, ShellStartupScriptProvider } from './shells/startupProvider';
1818
import {
1919
DidChangeTerminalActivationStateEvent,
2020
TerminalActivation,
@@ -68,7 +68,8 @@ export class TerminalManagerImpl implements TerminalManager {
6868

6969
constructor(
7070
private readonly ta: TerminalActivationInternal,
71-
private readonly startupProviders: ShellEnvsProvider[],
71+
private readonly startupEnvProviders: ShellEnvsProvider[],
72+
private readonly startupScriptProviders: ShellStartupScriptProvider[],
7273
) {
7374
this.disposables.push(
7475
this.onTerminalOpenedEmitter,
@@ -105,9 +106,20 @@ export class TerminalManagerImpl implements TerminalManager {
105106
private async autoActivateOnTerminalOpen(terminal: Terminal, environment: PythonEnvironment): Promise<void> {
106107
let actType = getAutoActivationType();
107108
const shellType = identifyTerminalShell(terminal);
108-
if (shellType === 'shellStartup' && !this.startupProviders.some((p) => p.shellType === shellType)) {
109+
if (shellType === 'shellStartup' && !this.startupEnvProviders.some((p) => p.shellType === shellType)) {
109110
actType = 'command';
110111
traceInfo(`Shell startup not supported for ${shellType}, using command activation`);
112+
113+
const provider = this.startupScriptProviders.find((p) => p.shellType === shellType);
114+
if (provider) {
115+
traceVerbose(`Checking is shell profile is setup for ${shellType}`);
116+
if (await provider.isSetup()) {
117+
} else {
118+
}
119+
} else {
120+
actType = 'command';
121+
traceInfo(`No startup script provider found for ${shellType}, using command activation`);
122+
}
111123
}
112124
if (actType === 'command') {
113125
if (isActivatableEnvironment(environment)) {
@@ -137,7 +149,7 @@ export class TerminalManagerImpl implements TerminalManager {
137149
const autoActType = getAutoActivationType();
138150
let envVars = options.env;
139151
if (autoActType === 'shellStartup') {
140-
const vars = await Promise.all(this.startupProviders.map((p) => p.getEnvVariables(environment)));
152+
const vars = await Promise.all(this.startupEnvProviders.map((p) => p.getEnvVariables(environment)));
141153

142154
vars.forEach((varMap) => {
143155
if (varMap) {

0 commit comments

Comments
 (0)