Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 4bf0aa0

Browse files
Handle PowerShell failing to start with actionable fixes (#4532)
Significantly improves our handling of PowerShell failing to start for various reasons.
1 parent 266ac53 commit 4bf0aa0

File tree

3 files changed

+75
-34
lines changed

3 files changed

+75
-34
lines changed

‎src/features/GenerateBugReport.ts‎

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,9 @@ export class GenerateBugReportFeature implements vscode.Disposable {
3333
if (this.sessionManager.PowerShellExeDetails === undefined) {
3434
return "Session's PowerShell details are unknown!";
3535
}
36-
37-
const powerShellExePath = this.sessionManager.PowerShellExeDetails.exePath;
38-
const powerShellArgs = [ "-NoProfile", "-Command", "$PSVersionTable | Out-String" ];
39-
const child = child_process.spawnSync(powerShellExePath, powerShellArgs);
36+
const child = child_process.spawnSync(
37+
this.sessionManager.PowerShellExeDetails.exePath,
38+
["-NoProfile", "-NoLogo", "-Command", "$PSVersionTable | Out-String"]);
4039
// Replace semicolons as they'll cause the URI component to truncate
4140
return child.stdout.toString().trim().replace(";", ",");
4241
}

‎src/process.ts‎

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { ILogger } from "./logging";
88
import Settings = require("./settings");
99
import utils = require("./utils");
1010
import { IEditorServicesSessionDetails } from "./session";
11+
import { promisify } from "util";
1112

1213
export class PowerShellProcess {
1314
// This is used to warn the user that the extension is taking longer than expected to startup.
@@ -134,6 +135,13 @@ export class PowerShellProcess {
134135
return sessionDetails;
135136
}
136137

138+
// This function should only be used after a failure has occurred because it is slow!
139+
public async getVersionCli(): Promise<string> {
140+
const exec = promisify(cp.execFile);
141+
const { stdout } = await exec(this.exePath, ["-NoProfile", "-NoLogo", "-Command", "$PSVersionTable.PSVersion.ToString()"]);
142+
return stdout.trim();
143+
}
144+
137145
// Returns the process Id of the consoleTerminal
138146
public async getPid(): Promise<number | undefined> {
139147
if (!this.consoleTerminal) { return undefined; }
@@ -148,13 +156,13 @@ export class PowerShellProcess {
148156
// Clean up the session file
149157
this.logger.write("Terminating PowerShell process...");
150158

151-
await PowerShellProcess.deleteSessionFile(this.sessionFilePath);
159+
this.consoleTerminal?.dispose();
160+
this.consoleTerminal = undefined;
152161

153162
this.consoleCloseSubscription?.dispose();
154163
this.consoleCloseSubscription = undefined;
155164

156-
this.consoleTerminal?.dispose();
157-
this.consoleTerminal = undefined;
165+
await PowerShellProcess.deleteSessionFile(this.sessionFilePath);
158166
}
159167

160168
public sendKeyPress(): void {

‎src/session.ts‎

Lines changed: 61 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import {
2424
OperatingSystem, PowerShellExeFinder
2525
} from "./platform";
2626
import { LanguageClientConsumer } from "./languageClientConsumer";
27-
import { SemVer } from "semver";
27+
import { SemVer,satisfies } from "semver";
2828

2929
export enum SessionStatus {
3030
NeverStarted,
@@ -458,35 +458,57 @@ export class SessionManager implements Middleware {
458458
try {
459459
this.sessionDetails = await languageServerProcess.start("EditorServices");
460460
} catch (err) {
461-
this.setSessionFailure("PowerShell process failed to start: ", err instanceof Error ? err.message : "unknown");
461+
// We should kill the process in case it's stuck.
462+
void languageServerProcess.dispose();
463+
464+
// PowerShell never started, probably a bad version!
465+
const version = await languageServerProcess.getVersionCli();
466+
let shouldUpdate = true;
467+
if (satisfies(version, "<5.1.0")) {
468+
void this.setSessionFailedGetPowerShell(`PowerShell ${version} is not supported, please update!`);
469+
} else if (satisfies(version, ">=5.1.0 <6.0.0")) {
470+
void this.setSessionFailedGetPowerShell("It looks like you're trying to use Windows PowerShell, which is supported on a best-effort basis. Can you try PowerShell 7?");
471+
} else if (satisfies(version, ">=6.0.0 <7.2.0")) {
472+
void this.setSessionFailedGetPowerShell(`PowerShell ${version} has reached end-of-support, please update!`);
473+
} else {
474+
shouldUpdate = false;
475+
void this.setSessionFailedOpenBug("PowerShell language server process didn't start!");
476+
}
477+
if (shouldUpdate) {
478+
// Run the update notifier since it won't run later as we failed
479+
// to start, but we have enough details to do so now.
480+
const versionDetails: IPowerShellVersionDetails = {
481+
"version": version,
482+
"edition": "", // Unused by UpdatePowerShell
483+
"commit": version, // Actually used by UpdatePowerShell
484+
"architecture": process.arch // Best guess based off Code's architecture
485+
};
486+
const updater = new UpdatePowerShell(this, this.sessionSettings, this.logger, versionDetails);
487+
void updater.checkForUpdate();
488+
}
489+
return;
462490
}
463491

464-
if (this.sessionDetails?.status === "started") {
492+
if (this.sessionDetails.status === "started") {// Successful server start with a session file
465493
this.logger.write("Language server started.");
466494
try {
467495
await this.startLanguageClient(this.sessionDetails);
496+
return languageServerProcess;
468497
} catch (err) {
469-
this.setSessionFailure("Language client failed to start: ",err instanceof Error ? err.message : "unknown");
498+
voidthis.setSessionFailedOpenBug("Language client failed to start: "+(err instanceof Error ? err.message : "unknown"));
470499
}
471-
} else if (this.sessionDetails?.status === "failed") {
500+
} else if (this.sessionDetails.status === "failed") {// Server started but indicated it failed
472501
if (this.sessionDetails.reason === "unsupported") {
473-
this.setSessionFailure(
474-
"PowerShell language features are only supported on PowerShell version 5.1 and 7+. " +
475-
`The current version is ${this.sessionDetails.powerShellVersion}.`);
502+
void this.setSessionFailedGetPowerShell(`PowerShell ${this.sessionDetails.powerShellVersion} is not supported, please update!`);
476503
} else if (this.sessionDetails.reason === "languageMode") {
477-
this.setSessionFailure(
478-
"PowerShell language features are disabled due to an unsupported LanguageMode: " +
479-
`${this.sessionDetails.detail}`);
504+
this.setSessionFailure(`PowerShell language features are disabled due to an unsupported LanguageMode: ${this.sessionDetails.detail}`);
480505
} else {
481-
this.setSessionFailure(
482-
`PowerShell could not be started for an unknown reason '${this.sessionDetails.reason}'`);
506+
void this.setSessionFailedOpenBug(`PowerShell could not be started for an unknown reason: ${this.sessionDetails.reason}`);
483507
}
484508
} else {
485-
this.setSessionFailure(
486-
`Unknown session status '${this.sessionDetails?.status}' with reason '${this.sessionDetails?.reason}`);
509+
void this.setSessionFailedOpenBug(`PowerShell could not be started with an unknown status: ${this.sessionDetails.status}, and reason: ${this.sessionDetails.reason}`);
487510
}
488-
489-
return languageServerProcess;
511+
return;
490512
}
491513

492514
private async findPowerShell(): Promise<IPowerShellExeDetails | undefined> {
@@ -523,16 +545,7 @@ export class SessionManager implements Middleware {
523545
+ " Do you have PowerShell installed?"
524546
+ " You can also configure custom PowerShell installations"
525547
+ " with the 'powershell.powerShellAdditionalExePaths' setting.";
526-
527-
await this.logger.writeAndShowErrorWithActions(message, [
528-
{
529-
prompt: "Get PowerShell",
530-
action: async (): Promise<void> => {
531-
const getPSUri = vscode.Uri.parse("https://aka.ms/get-powershell-vscode");
532-
await vscode.env.openExternal(getPSUri);
533-
},
534-
},
535-
]);
548+
void this.setSessionFailedGetPowerShell(message);
536549
}
537550

538551
return foundPowerShell;
@@ -791,6 +804,27 @@ Type 'help' to get help.
791804
void this.logger.writeAndShowError(message, ...additionalMessages);
792805
}
793806

807+
private async setSessionFailedOpenBug(message: string): Promise<void> {
808+
this.setSessionStatus("Initialization Error!", SessionStatus.Failed);
809+
await this.logger.writeAndShowErrorWithActions(message, [{
810+
prompt: "Open an Issue",
811+
action: async (): Promise<void> => {
812+
await vscode.commands.executeCommand("PowerShell.GenerateBugReport");
813+
}}]
814+
);
815+
}
816+
817+
private async setSessionFailedGetPowerShell(message: string): Promise<void> {
818+
this.setSessionStatus("Initialization Error!", SessionStatus.Failed);
819+
await this.logger.writeAndShowErrorWithActions(message, [{
820+
prompt: "Open PowerShell Install Documentation",
821+
action: async (): Promise<void> => {
822+
await vscode.env.openExternal(
823+
vscode.Uri.parse("https://aka.ms/get-powershell-vscode"));
824+
}}]
825+
);
826+
}
827+
794828
private async changePowerShellDefaultVersion(exePath: IPowerShellExeDetails): Promise<void> {
795829
this.suppressRestartPrompt = true;
796830
try {

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /