diff --git a/package.json b/package.json index f2daee6b4b..61100d285a 100644 --- a/package.json +++ b/package.json @@ -400,6 +400,33 @@ "isExecutable": true, "description": "Specifies the full path to a PowerShell executable. Changes the installation of PowerShell used for language and debugging services." }, + "powershell.powerShellAdditionalExePaths": { + "type":"array", + "description": "Specifies an array of versionName / exePath pairs where exePath points to a non-standard install location for PowerShell and versionName can be used to reference this path with the powershell.powerShellDefaultVersion setting.", + "isExecutable": true, + "uniqueItems": true, + "items": { + "type": "object", + "required":[ + "versionName", + "exePath" + ], + "properties": { + "versionName": { + "type":"string", + "description": "Specifies the version name of this PowerShell executable. The version name can be referenced via the powershell.powerShellDefaultVersion setting." + }, + "exePath": { + "type":"string", + "description": "Specifies the path to the PowerShell executable. Typically this is a path to a non-standard install location." + } + } + } + }, + "powershell.powerShellDefaultVersion": { + "type":"string", + "description": "Specifies the name of the PowerShell version used in the startup session when the extension loads e.g \"Windows PowerShell (x86)\" or \"PowerShell Core 6.0.2 (x64)\"." + }, "powershell.startAutomatically": { "type": "boolean", "default": true, diff --git a/src/platform.ts b/src/platform.ts index b561cf614c..73e4bb1804 100644 --- a/src/platform.ts +++ b/src/platform.ts @@ -108,14 +108,13 @@ export function fixWindowsPowerShellPath(powerShellExePath: string, platformDeta return powerShellExePath; } -export function getAvailablePowerShellExes(platformDetails: IPlatformDetails): IPowerShellExeDetails[] { +export function getAvailablePowerShellExes( + platformDetails: IPlatformDetails, + sessionSettings: Settings.ISettings | undefined): IPowerShellExeDetails[] { let paths: IPowerShellExeDetails[] = []; if (platformDetails.operatingSystem === OperatingSystem.Windows) { - const psCoreInstallPath = - (!platformDetails.isProcess64Bit ? process.env.ProgramW6432 : process.env.ProgramFiles) + "\\PowerShell"; - if (platformDetails.isProcess64Bit) { paths.push({ versionName: WindowsPowerShell64BitLabel, @@ -140,33 +139,45 @@ export function getAvailablePowerShellExes(platformDetails: IPlatformDetails): I }); } + const psCoreInstallPath = + (!platformDetails.isProcess64Bit ? process.env.ProgramW6432 : process.env.ProgramFiles) + "\\PowerShell"; + if (fs.existsSync(psCoreInstallPath)) { + const arch = platformDetails.isProcess64Bit ? "(x64)" : "(x86)"; const psCorePaths = fs.readdirSync(psCoreInstallPath) .map((item) => path.join(psCoreInstallPath, item)) - .filter((item) => fs.lstatSync(item).isDirectory()) - .map((item) => { - let exePath = path.join(item, "pwsh.exe"); - if (!fs.existsSync(exePath)) { - exePath = path.join(item, "powershell.exe"); - } - - return { - versionName: `PowerShell Core ${path.parse(item).base}`, - exePath, - }; - }); + .filter((item) => { + const exePath = path.join(item, "pwsh.exe"); + return fs.lstatSync(item).isDirectory() && fs.existsSync(exePath); + }) + .map((item) => ({ + versionName: `PowerShell Core ${path.parse(item).base} ${arch}`, + exePath: path.join(item, "pwsh.exe"), + })); if (psCorePaths) { paths = paths.concat(psCorePaths); } } } else { + // Handle Linux and macOS case paths.push({ versionName: "PowerShell Core", exePath: this.getDefaultPowerShellPath(platformDetails), }); } + // When unit testing, we don't have session settings to test so skip reading this setting + if (sessionSettings) { + // Add additional PowerShell paths as configured in settings + for (const additionalPowerShellExePath of sessionSettings.powerShellAdditionalExePaths) { + paths.push({ + versionName: additionalPowerShellExePath.versionName, + exePath: additionalPowerShellExePath.exePath, + }); + } + } + return paths; } diff --git a/src/session.ts b/src/session.ts index 446adf8a89..22130e5d9a 100644 --- a/src/session.ts +++ b/src/session.ts @@ -26,6 +26,7 @@ import { getPlatformDetails, IPlatformDetails, OperatingSystem } from "./platform"; export enum SessionStatus { + NeverStarted, NotStarted, Initializing, Running, @@ -40,7 +41,7 @@ export class SessionManager implements Middleware { private hostVersion: string; private editorServicesArgs: string; private powerShellExePath: string = ""; - private sessionStatus: SessionStatus; + private sessionStatus: SessionStatus = SessionStatus.NeverStarted; private suppressRestartPrompt: boolean; private focusConsoleOnExecute: boolean; private platformDetails: IPlatformDetails; @@ -246,6 +247,7 @@ export class SessionManager implements Middleware { } public getPowerShellExePath(): string { + let powerShellExePath: string; if (!this.sessionSettings.powerShellExePath && this.sessionSettings.developer.powerShellExePath) { @@ -280,11 +282,27 @@ export class SessionManager implements Middleware { }); } + // If powershell.powerShellDefaultVersion specified, attempt to find the PowerShell exe path + // of the version specified by the setting. + if ((this.sessionStatus === SessionStatus.NeverStarted) && this.sessionSettings.powerShellDefaultVersion) { + const powerShellExePaths = getAvailablePowerShellExes(this.platformDetails, this.sessionSettings); + const powerShellDefaultVersion = + powerShellExePaths.find((item) => item.versionName === this.sessionSettings.powerShellDefaultVersion); + + if (powerShellDefaultVersion) { + powerShellExePath = powerShellDefaultVersion.exePath; + } else { + this.log.writeWarning( + `Could not find powerShellDefaultVersion: '${this.sessionSettings.powerShellDefaultVersion}'`); + } + } + // Is there a setting override for the PowerShell path? - let powerShellExePath = - (this.sessionSettings.powerShellExePath || - this.sessionSettings.developer.powerShellExePath || - "").trim(); + powerShellExePath = + (powerShellExePath || + this.sessionSettings.powerShellExePath || + this.sessionSettings.developer.powerShellExePath || + "").trim(); // New versions of PS Core uninstall the previous version // so make sure the path stored in the settings exists. @@ -718,12 +736,25 @@ export class SessionManager implements Middleware { private showSessionMenu() { let menuItems: SessionMenuItem[] = []; + const currentExePath = (this.powerShellExePath || "").toLowerCase(); + const availablePowerShellExes = + getAvailablePowerShellExes(this.platformDetails, this.sessionSettings); + if (this.sessionStatus === SessionStatus.Running) { + const currentPowerShellExe = + availablePowerShellExes + .find((item) => item.exePath.toLowerCase() === currentExePath); + + const powerShellSessionName = + currentPowerShellExe ? + currentPowerShellExe.versionName : + `PowerShell ${this.versionDetails.displayVersion} ` + + `(${this.versionDetails.architecture}) ${this.versionDetails.edition} Edition ` + + `[${this.versionDetails.version}]`; + menuItems = [ new SessionMenuItem( - `Current session: PowerShell ${this.versionDetails.displayVersion} ` + - `(${this.versionDetails.architecture}) ${this.versionDetails.edition} Edition ` + - `[${this.versionDetails.version}]`, + `Current session: ${powerShellSessionName}`, () => { vscode.commands.executeCommand("PowerShell.ShowLogs"); }), new SessionMenuItem( @@ -738,9 +769,8 @@ export class SessionManager implements Middleware { ]; } - const currentExePath = (this.powerShellExePath || "").toLowerCase(); const powerShellItems = - getAvailablePowerShellExes(this.platformDetails) + availablePowerShellExes .filter((item) => item.exePath.toLowerCase() !== currentExePath) .map((item) => { return new SessionMenuItem( diff --git a/src/settings.ts b/src/settings.ts index a9a626562c..ba42119c24 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -14,6 +14,11 @@ enum CodeFormattingPreset { Stroustrup, } +export interface IPowerShellAdditionalExePathSettings { + versionName: string; + exePath: string; +} + export interface IBugReportingSettings { project: string; } @@ -50,6 +55,8 @@ export interface IDeveloperSettings { } export interface ISettings { + powerShellAdditionalExePaths?: IPowerShellAdditionalExePathSettings[]; + powerShellDefaultVersion?: string; powerShellExePath?: string; startAutomatically?: boolean; useX86Host?: boolean; @@ -115,6 +122,10 @@ export function load(): ISettings { return { startAutomatically: configuration.get("startAutomatically", true), + powerShellAdditionalExePaths: + configuration.get("powerShellAdditionalExePaths", undefined), + powerShellDefaultVersion: + configuration.get("powerShellDefaultVersion", undefined), powerShellExePath: configuration.get("powerShellExePath", undefined), useX86Host: diff --git a/test/platform.test.ts b/test/platform.test.ts index 8ca3ab51cf..b13cb303ae 100644 --- a/test/platform.test.ts +++ b/test/platform.test.ts @@ -21,7 +21,7 @@ function checkAvailableWindowsPowerShellPaths( // The system may return PowerShell Core paths so only // enumerate the first list items. - const enumeratedPaths = platform.getAvailablePowerShellExes(platformDetails); + const enumeratedPaths = platform.getAvailablePowerShellExes(platformDetails, undefined); for (let i; i < expectedPaths.length; i++) { assert.equal(enumeratedPaths[i], expectedPaths[i]); }