diff --git a/package.json b/package.json index 0931fb3fb9..9abea534e6 100644 --- a/package.json +++ b/package.json @@ -550,6 +550,16 @@ "description": "Specifies whether you should be prompted to update your version of PowerShell.", "default": true }, + "powershell.startAsLoginShell.osx": { + "type": "boolean", + "default": true, + "description": "Starts the PowerShell extension's underlying PowerShell process as a login shell, if applicable." + }, + "powershell.startAsLoginShell.linux": { + "type": "boolean", + "default": false, + "description": "Starts the PowerShell extension's underlying PowerShell process as a login shell, if applicable." + }, "powershell.startAutomatically": { "type": "boolean", "default": true, @@ -735,11 +745,6 @@ "type": "array", "default": null, "description": "An array of strings that enable experimental features in the PowerShell extension." - }, - "powershell.developer.powerShellExeIsWindowsDevBuild": { - "type": "boolean", - "default": false, - "description": "Indicates that the powerShellExePath points to a developer build of Windows PowerShell and configures it for development." } } }, diff --git a/src/process.ts b/src/process.ts index aefcd451f1..3138c6cfdb 100644 --- a/src/process.ts +++ b/src/process.ts @@ -63,10 +63,19 @@ export class PowerShellProcess { this.startArgs += "-UseLegacyReadLine"; } - const powerShellArgs = [ - "-NoProfile", - "-NonInteractive", - ]; + const powerShellArgs = []; + + const useLoginShell: boolean = + (utils.isMacOS && this.sessionSettings.startAsLoginShell.osx) + || (utils.isLinux && this.sessionSettings.startAsLoginShell.linux); + + if (useLoginShell && this.isLoginShell(this.exePath)) { + // This MUST be the first argument. + powerShellArgs.push("-Login"); + } + + powerShellArgs.push("-NoProfile"); + powerShellArgs.push("-NonInteractive"); // Only add ExecutionPolicy param on Windows if (utils.isWindows) { @@ -88,27 +97,11 @@ export class PowerShellProcess { Buffer.from(startEditorServices, "utf16le").toString("base64")); } - let powerShellExePath = this.exePath; - - if (this.sessionSettings.developer.powerShellExeIsWindowsDevBuild) { - // Windows PowerShell development builds need the DEVPATH environment - // variable set to the folder where development binaries are held - - // NOTE: This batch file approach is needed temporarily until VS Code's - // createTerminal API gets an argument for setting environment variables - // on the launched process. - const batScriptPath = path.resolve(__dirname, "../../sessions/powershell.bat"); - fs.writeFileSync( - batScriptPath, - `@set DEVPATH=${path.dirname(powerShellExePath)}\r\n@${powerShellExePath} %*`); - - powerShellExePath = batScriptPath; - } - this.log.write( "Language server starting --", - " exe: " + powerShellExePath, - " args: " + startEditorServices); + " PowerShell executable: " + this.exePath, + " PowerShell args: " + powerShellArgs.join(" "), + " PowerShell Editor Services args: " + startEditorServices); // Make sure no old session file exists utils.deleteSessionFile(this.sessionFilePath); @@ -117,7 +110,7 @@ export class PowerShellProcess { this.consoleTerminal = vscode.window.createTerminal( this.title, - powerShellExePath, + this.exePath, powerShellArgs); if (this.sessionSettings.integratedConsole.showOnStartup) { @@ -178,4 +171,18 @@ export class PowerShellProcess { this.consoleTerminal = undefined; } } + + private isLoginShell(pwshPath: string): boolean { + try { + // We can't know what version of PowerShell we have without running it + // So we try to start PowerShell with -Login + // If it exits successfully, we return true + // If it exits unsuccessfully, node throws, we catch, and return false + cp.execFileSync(pwshPath, ["-Login", "-NoProfile", "-NoLogo", "-Command", "exit 0"]); + } catch { + return false; + } + + return true; + } } diff --git a/src/settings.ts b/src/settings.ts index d57a9cedf7..090c9e08b5 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -72,7 +72,6 @@ export interface IDeveloperSettings { bundledModulesPath?: string; editorServicesLogLevel?: string; editorServicesWaitForDebugger?: boolean; - powerShellExeIsWindowsDevBuild?: boolean; } export interface ISettings { @@ -82,6 +81,7 @@ export interface ISettings { powerShellExePath?: string; promptToUpdatePowerShell?: boolean; bundledModulesPath?: string; + startAsLoginShell?: IStartAsLoginShellSettings; startAutomatically?: boolean; useX86Host?: boolean; enableProfileLoading?: boolean; @@ -96,6 +96,11 @@ export interface ISettings { sideBar?: ISideBarSettings; } +export interface IStartAsLoginShellSettings { + osx?: boolean; + linux?: boolean; +} + export interface IIntegratedConsoleSettings { showOnStartup?: boolean; focusConsoleOnExecute?: boolean; @@ -130,7 +135,6 @@ export function load(): ISettings { bundledModulesPath: "../../../PowerShellEditorServices/module", editorServicesLogLevel: "Normal", editorServicesWaitForDebugger: false, - powerShellExeIsWindowsDevBuild: false, }; const defaultCodeFoldingSettings: ICodeFoldingSettings = { @@ -156,6 +160,11 @@ export function load(): ISettings { useCorrectCasing: false, }; + const defaultStartAsLoginShellSettings: IStartAsLoginShellSettings = { + osx: true, + linux: false, + }; + const defaultIntegratedConsoleSettings: IIntegratedConsoleSettings = { showOnStartup: true, focusConsoleOnExecute: true, @@ -202,6 +211,13 @@ export function load(): ISettings { configuration.get("bugReporting", defaultBugReportingSettings), sideBar: configuration.get("sideBar", defaultSideBarSettings), + startAsLoginShell: + // tslint:disable-next-line + // We follow the same convention as VS Code - https://github.com/microsoft/vscode/blob/ff00badd955d6cfcb8eab5f25f3edc86b762f49f/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts#L105-L107 + // "Unlike on Linux, ~/.profile is not sourced when logging into a macOS session. This + // is the reason terminals on macOS typically run login shells by default which set up + // the environment. See http://unix.stackexchange.com/a/119675/115410" + configuration.get("startAsLoginShell", defaultStartAsLoginShellSettings), }; }