Skip to content

Commit 25bd8af

Browse files
Merge pull request #4064 from PowerShell/andschwa/cwd
Add multi-root choice experience to `powershell.cwd`
2 parents 92f5f6d + 0da7e92 commit 25bd8af

12 files changed

+146
-87
lines changed

package.json

+3-2
Original file line numberDiff line numberDiff line change
@@ -798,10 +798,11 @@
798798
"Verbose",
799799
"Normal",
800800
"Warning",
801-
"Error"
801+
"Error",
802+
"None"
802803
],
803804
"default": "Normal",
804-
"description": "Sets the logging verbosity level for the PowerShell Editor Services host executable. Valid values are 'Diagnostic', 'Verbose', 'Normal', 'Warning', and 'Error'"
805+
"description": "Sets the logging verbosity level for the PowerShell Editor Services host executable. Valid values are 'Diagnostic', 'Verbose', 'Normal', 'Warning', 'Error', and 'None'"
805806
},
806807
"powershell.developer.editorServicesWaitForDebugger": {
807808
"type": "boolean",

src/features/ExternalApi.ts

+5
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export interface IPowerShellExtensionClient {
1919
unregisterExternalExtension(uuid: string): boolean;
2020
getPowerShellVersionDetails(uuid: string): Promise<IExternalPowerShellDetails>;
2121
waitUntilStarted(uuid: string): Promise<void>;
22+
getStorageUri(): vscode.Uri;
2223
}
2324

2425
/*
@@ -166,6 +167,10 @@ export class ExternalApiFeature extends LanguageClientConsumer implements IPower
166167
return this.sessionManager.waitUntilStarted();
167168
}
168169

170+
public getStorageUri(): vscode.Uri {
171+
return this.extensionContext.storageUri;
172+
}
173+
169174
public dispose() {
170175
// Nothing to dispose.
171176
}

src/features/UpdatePowerShell.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -164,9 +164,9 @@ export async function InvokePowerShellUpdateCheck(
164164
// Invoke the MSI via cmd.
165165
const msi = spawn("msiexec", ["/i", msiDownloadPath]);
166166

167-
msi.on("close", (code) => {
167+
msi.on("close", async () => {
168168
// Now that the MSI is finished, start the Integrated Console session.
169-
sessionManager.start();
169+
await sessionManager.start();
170170
fs.unlinkSync(msiDownloadPath);
171171
});
172172

src/logging.ts

+21-18
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ export enum LogLevel {
1313
Normal,
1414
Warning,
1515
Error,
16+
None,
1617
}
1718

1819
/** Interface for logging operations. New features should use this interface for the "type" of logger.
@@ -29,19 +30,24 @@ export interface ILogger {
2930

3031
export class Logger implements ILogger {
3132

32-
public logBasePath: string;
33-
public logSessionPath: string;
33+
public logBasePath: vscode.Uri;
34+
public logSessionPath: vscode.Uri;
3435
public MinimumLogLevel: LogLevel = LogLevel.Normal;
3536

3637
private commands: vscode.Disposable[];
3738
private logChannel: vscode.OutputChannel;
38-
private logFilePath: string;
39+
private logFilePath: vscode.Uri;
3940

40-
constructor() {
41+
constructor(logBasePath: vscode.Uri) {
4142
this.logChannel = vscode.window.createOutputChannel("PowerShell Extension Logs");
4243

43-
this.logBasePath = path.resolve(__dirname, "../logs");
44-
utils.ensurePathExists(this.logBasePath);
44+
if (logBasePath === undefined) {
45+
// No workspace, we have to use another folder.
46+
this.logBasePath = vscode.Uri.file(path.resolve(__dirname, "../logs"));
47+
utils.ensurePathExists(this.logBasePath.fsPath);
48+
} else {
49+
this.logBasePath = vscode.Uri.joinPath(logBasePath, "logs");
50+
}
4551

4652
this.commands = [
4753
vscode.commands.registerCommand(
@@ -59,8 +65,8 @@ export class Logger implements ILogger {
5965
this.logChannel.dispose();
6066
}
6167

62-
public getLogFilePath(baseName: string): string {
63-
return path.resolve(this.logSessionPath, `${baseName}.log`);
68+
public getLogFilePath(baseName: string): vscode.Uri {
69+
return vscode.Uri.joinPath(this.logSessionPath, `${baseName}.log`);
6470
}
6571

6672
public writeAtLevel(logLevel: LogLevel, message: string, ...additionalMessages: string[]) {
@@ -136,17 +142,16 @@ export class Logger implements ILogger {
136142
}
137143
}
138144

139-
public startNewLog(minimumLogLevel: string = "Normal") {
145+
public async startNewLog(minimumLogLevel: string = "Normal") {
140146
this.MinimumLogLevel = this.logLevelNameToValue(minimumLogLevel.trim());
141147

142148
this.logSessionPath =
143-
path.resolve(
149+
vscode.Uri.joinPath(
144150
this.logBasePath,
145151
`${Math.floor(Date.now() / 1000)}-${vscode.env.sessionId}`);
146152

147153
this.logFilePath = this.getLogFilePath("vscode-powershell");
148-
149-
utils.ensurePathExists(this.logSessionPath);
154+
await vscode.workspace.fs.createDirectory(this.logSessionPath);
150155
}
151156

152157
private logLevelNameToValue(logLevelName: string): LogLevel {
@@ -156,6 +161,7 @@ export class Logger implements ILogger {
156161
case "normal": return LogLevel.Normal;
157162
case "warning": return LogLevel.Warning;
158163
case "error": return LogLevel.Error;
164+
case "none": return LogLevel.None;
159165
default: return LogLevel.Normal;
160166
}
161167
}
@@ -168,10 +174,7 @@ export class Logger implements ILogger {
168174
if (this.logSessionPath) {
169175
// Open the folder in VS Code since there isn't an easy way to
170176
// open the folder in the platform's file browser
171-
vscode.commands.executeCommand(
172-
"vscode.openFolder",
173-
vscode.Uri.file(this.logSessionPath),
174-
true);
177+
vscode.commands.executeCommand("vscode.openFolder", this.logSessionPath, true);
175178
}
176179
}
177180

@@ -181,9 +184,9 @@ export class Logger implements ILogger {
181184
`${now.toLocaleDateString()} ${now.toLocaleTimeString()} [${LogLevel[level].toUpperCase()}] - ${message}`;
182185

183186
this.logChannel.appendLine(timestampedMessage);
184-
if (this.logFilePath) {
187+
if (this.logFilePath && this.MinimumLogLevel !== LogLevel.None) {
185188
fs.appendFile(
186-
this.logFilePath,
189+
this.logFilePath.fsPath,
187190
timestampedMessage + os.EOL,
188191
(err) => {
189192
if (err) {

src/main.ts

+13-9
Original file line numberDiff line numberDiff line change
@@ -52,19 +52,25 @@ const documentSelector: DocumentSelector = [
5252
{ language: "powershell", scheme: "untitled" },
5353
];
5454

55-
export function activate(context: vscode.ExtensionContext): IPowerShellExtensionClient {
56-
// create telemetry reporter on extension activation
55+
// NOTE: Now that this is async, we can probably improve a lot!
56+
export async function activate(context: vscode.ExtensionContext): Promise<IPowerShellExtensionClient> {
5757
telemetryReporter = new TelemetryReporter(PackageJSON.name, PackageJSON.version, AI_KEY);
5858

59-
// If both extensions are enabled, this will cause unexpected behavior since both register the same commands
59+
// If both extensions are enabled, this will cause unexpected behavior since both register the same commands.
60+
// TODO: Merge extensions and use preview channel in marketplace instead.
6061
if (PackageJSON.name.toLowerCase() === "powershell-preview"
6162
&& vscode.extensions.getExtension("ms-vscode.powershell")) {
6263
vscode.window.showWarningMessage(
6364
"'PowerShell' and 'PowerShell Preview' are both enabled. Please disable one for best performance.");
6465
}
6566

67+
// This displays a popup and a changelog after an update.
6668
checkForUpdatedVersion(context, PackageJSON.version);
6769

70+
// Load and validate settings (will prompt for 'cwd' if necessary).
71+
await Settings.validateCwdSetting();
72+
const extensionSettings = Settings.load();
73+
6874
vscode.languages.setLanguageConfiguration(
6975
PowerShellLanguageId,
7076
{
@@ -118,11 +124,8 @@ export function activate(context: vscode.ExtensionContext): IPowerShellExtension
118124
],
119125
});
120126

121-
// Create the logger
122-
logger = new Logger();
123-
124-
// Set the log level
125-
const extensionSettings = Settings.load();
127+
// Setup the logger.
128+
logger = new Logger(context.storageUri);
126129
logger.MinimumLogLevel = LogLevel[extensionSettings.developer.editorServicesLogLevel];
127130

128131
sessionManager =
@@ -169,14 +172,15 @@ export function activate(context: vscode.ExtensionContext): IPowerShellExtension
169172
sessionManager.setLanguageClientConsumers(languageClientConsumers);
170173

171174
if (extensionSettings.startAutomatically) {
172-
sessionManager.start();
175+
await sessionManager.start();
173176
}
174177

175178
return {
176179
registerExternalExtension: (id: string, apiVersion: string = 'v1') => externalApi.registerExternalExtension(id, apiVersion),
177180
unregisterExternalExtension: uuid => externalApi.unregisterExternalExtension(uuid),
178181
getPowerShellVersionDetails: uuid => externalApi.getPowerShellVersionDetails(uuid),
179182
waitUntilStarted: uuid => externalApi.waitUntilStarted(uuid),
183+
getStorageUri: () => externalApi.getStorageUri(),
180184
};
181185
}
182186

src/process.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ export class PowerShellProcess {
5151
: "";
5252

5353
this.startPsesArgs +=
54-
`-LogPath '${PowerShellProcess.escapeSingleQuotes(editorServicesLogPath)}' ` +
54+
`-LogPath '${PowerShellProcess.escapeSingleQuotes(editorServicesLogPath.fsPath)}' ` +
5555
`-SessionDetailsPath '${PowerShellProcess.escapeSingleQuotes(this.sessionFilePath)}' ` +
5656
`-FeatureFlags @(${featureFlags}) `;
5757

src/session.ts

+27-21
Original file line numberDiff line numberDiff line change
@@ -103,13 +103,15 @@ export class SessionManager implements Middleware {
103103
this.languageClientConsumers = languageClientConsumers;
104104
}
105105

106-
public start(exeNameOverride?: string) {
106+
public async start(exeNameOverride?: string) {
107+
await Settings.validateCwdSetting();
107108
this.sessionSettings = Settings.load();
109+
108110
if (exeNameOverride) {
109111
this.sessionSettings.powerShellDefaultVersion = exeNameOverride;
110112
}
111113

112-
this.log.startNewLog(this.sessionSettings.developer.editorServicesLogLevel);
114+
await this.log.startNewLog(this.sessionSettings.developer.editorServicesLogLevel);
113115

114116
// Create the PowerShell executable finder now
115117
this.powershellExeFinder = new PowerShellExeFinder(
@@ -240,9 +242,9 @@ export class SessionManager implements Middleware {
240242
this.sessionStatus = SessionStatus.NotStarted;
241243
}
242244

243-
public restartSession(exeNameOverride?: string) {
245+
public async restartSession(exeNameOverride?: string) {
244246
this.stop();
245-
this.start(exeNameOverride);
247+
await this.start(exeNameOverride);
246248
}
247249

248250
public getSessionDetails(): utils.IEditorServicesSessionDetails {
@@ -387,14 +389,16 @@ export class SessionManager implements Middleware {
387389
}
388390
}
389391

390-
private onConfigurationUpdated() {
392+
private async onConfigurationUpdated() {
391393
const settings = Settings.load();
392394

393395
this.focusConsoleOnExecute = settings.integratedConsole.focusConsoleOnExecute;
394396

395397
// Detect any setting changes that would affect the session
396398
if (!this.suppressRestartPrompt &&
397-
(settings.powerShellDefaultVersion.toLowerCase() !==
399+
(settings.cwd.toLowerCase() !==
400+
this.sessionSettings.cwd.toLowerCase() ||
401+
settings.powerShellDefaultVersion.toLowerCase() !==
398402
this.sessionSettings.powerShellDefaultVersion.toLowerCase() ||
399403
settings.developer.editorServicesLogLevel.toLowerCase() !==
400404
this.sessionSettings.developer.editorServicesLogLevel.toLowerCase() ||
@@ -403,14 +407,13 @@ export class SessionManager implements Middleware {
403407
settings.integratedConsole.useLegacyReadLine !==
404408
this.sessionSettings.integratedConsole.useLegacyReadLine)) {
405409

406-
vscode.window.showInformationMessage(
410+
const response: string = await vscode.window.showInformationMessage(
407411
"The PowerShell runtime configuration has changed, would you like to start a new session?",
408-
"Yes", "No")
409-
.then((response) => {
412+
"Yes", "No");
413+
410414
if (response === "Yes") {
411-
this.restartSession();
415+
await this.restartSession();
412416
}
413-
});
414417
}
415418
}
416419

@@ -433,7 +436,7 @@ export class SessionManager implements Middleware {
433436
this.registeredCommands = [
434437
vscode.commands.registerCommand("PowerShell.RestartSession", () => { this.restartSession(); }),
435438
vscode.commands.registerCommand(this.ShowSessionMenuCommandName, () => { this.showSessionMenu(); }),
436-
vscode.workspace.onDidChangeConfiguration(() => this.onConfigurationUpdated()),
439+
vscode.workspace.onDidChangeConfiguration(async () => { await this.onConfigurationUpdated(); }),
437440
vscode.commands.registerCommand(
438441
"PowerShell.ShowSessionConsole", (isExecute?: boolean) => { this.showSessionConsole(isExecute); }),
439442
];
@@ -457,10 +460,10 @@ export class SessionManager implements Middleware {
457460
this.sessionSettings);
458461

459462
this.languageServerProcess.onExited(
460-
() => {
463+
async () => {
461464
if (this.sessionStatus === SessionStatus.Running) {
462465
this.setSessionStatus("Session Exited", SessionStatus.Failed);
463-
this.promptForRestart();
466+
await this.promptForRestart();
464467
}
465468
});
466469

@@ -503,11 +506,14 @@ export class SessionManager implements Middleware {
503506
});
504507
}
505508

506-
private promptForRestart() {
507-
vscode.window.showErrorMessage(
509+
private async promptForRestart() {
510+
const response: string = await vscode.window.showErrorMessage(
508511
"The PowerShell Integrated Console (PSIC) has stopped, would you like to restart it? (IntelliSense will not work unless the PSIC is active and unblocked.)",
509-
"Yes", "No")
510-
.then((answer) => { if (answer === "Yes") { this.restartSession(); }});
512+
"Yes", "No");
513+
514+
if (response === "Yes") {
515+
await this.restartSession();
516+
}
511517
}
512518

513519
private startLanguageClient(sessionDetails: utils.IEditorServicesSessionDetails) {
@@ -756,7 +762,7 @@ export class SessionManager implements Middleware {
756762
// rather than pull from the settings. The issue we prevent here is when a
757763
// workspace setting is defined which gets priority over user settings which
758764
// is what the change above sets.
759-
this.restartSession(exePath.displayName);
765+
await this.restartSession(exePath.displayName);
760766
}
761767

762768
private showSessionConsole(isExecute?: boolean) {
@@ -817,10 +823,10 @@ export class SessionManager implements Middleware {
817823

818824
new SessionMenuItem(
819825
"Restart Current Session",
820-
() => {
826+
async () => {
821827
// We pass in the display name so we guarantee that the session
822828
// will be the same PowerShell.
823-
this.restartSession(this.PowerShellExeDetails.displayName);
829+
await this.restartSession(this.PowerShellExeDetails.displayName);
824830
}),
825831

826832
new SessionMenuItem(

0 commit comments

Comments
 (0)