Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

handle bash login args #141467

Merged
merged 15 commits into from
Jan 25, 2022
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

if [ -z "$VSCODE_SHELL_LOGIN" ]; then
if [ -z "$OS_IS_MAC" ]; then
. ~/.bashrc
else
# Imitate -l because --init-file doesn't support it:
Expand Down
38 changes: 4 additions & 34 deletions src/vs/workbench/contrib/terminal/browser/terminalInstance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,6 @@ import { fromNow } from 'vs/base/common/date';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { TerminalCapabilityStoreMultiplexer } from 'vs/workbench/contrib/terminal/common/capabilities/terminalCapabilityStore';
import { TerminalCapability } from 'vs/workbench/contrib/terminal/common/capabilities/capabilities';
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
import { injectShellIntegrationArgs } from 'vs/workbench/contrib/terminal/common/terminalEnvironment';

const enum Constants {
/**
Expand Down Expand Up @@ -328,8 +326,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
@IWorkspaceContextService private readonly _workspaceContextService: IWorkspaceContextService,
@IEditorService private readonly _editorService: IEditorService,
@IWorkspaceTrustRequestService private readonly _workspaceTrustRequestService: IWorkspaceTrustRequestService,
@ICommandService private readonly _commandService: ICommandService,
@IRemoteAgentService private readonly _remoteAgentService: IRemoteAgentService
@ICommandService private readonly _commandService: ICommandService
) {
super();

Expand Down Expand Up @@ -393,7 +390,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
// Wait for a period to allow a container to be ready
await this._containerReadyBarrier.wait();
if (this._configHelper.config.enableShellIntegration && !this.shellLaunchConfig.executable) {
const os = await this._getBackendOS();
const os = await this._processManager.getBackendOS();
this.shellLaunchConfig.executable = (await this._terminalProfileResolverService.getDefaultProfile({ remoteAuthority: this.remoteAuthority, os })).path;
}
await this._createProcess();
Expand Down Expand Up @@ -1282,13 +1279,10 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
}

const hadIcon = !!this.shellLaunchConfig.icon;
const isBackendWindows = await this._backendIsWindows();
const shellIntegration = injectShellIntegrationArgs(this._logService, this._configHelper.config.enableShellIntegration, this.shellLaunchConfig, isBackendWindows);
this.shellLaunchConfig.args = shellIntegration.args;
const enableShellIntegration = shellIntegration.enableShellIntegration;

await this._processManager.createProcess(this._shellLaunchConfig, this._cols || Constants.DefaultCols, this._rows || Constants.DefaultRows, this._accessibilityService.isScreenReaderOptimized()).then(error => {
if (error) {
this._onProcessExit(error, enableShellIntegration);
this._onProcessExit(error, error.code === 1337);
}
});
if (this.xterm?.shellIntegration) {
Expand All @@ -1299,30 +1293,6 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
}
}

private async _backendIsWindows(): Promise<boolean> {
let os = OS;
if (!!this.remoteAuthority) {
const remoteEnv = await this._remoteAgentService.getEnvironment();
if (!remoteEnv) {
throw new Error(`Failed to get remote environment for remote authority "${this.remoteAuthority}"`);
}
os = remoteEnv.os;
}
return os === OperatingSystem.Windows;
}

private async _getBackendOS(): Promise<OperatingSystem> {
let os = OS;
if (!!this.remoteAuthority) {
const remoteEnv = await this._remoteAgentService.getEnvironment();
if (!remoteEnv) {
throw new Error(`Failed to get remote environment for remote authority "${this.remoteAuthority}"`);
}
os = remoteEnv.os;
}
return os;
}

private _onProcessData(ev: IProcessDataEvent): void {
const messageId = ++this._latestXtermWriteData;
if (ev.trackCommit) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce
isDisconnected: boolean = false;
environmentVariableInfo: IEnvironmentVariableInfo | undefined;
backend: ITerminalBackend | undefined;
shellIntegrationAttempted: boolean = false;
readonly capabilities = new TerminalCapabilityStore();

private _isDisposed: boolean = false;
Expand Down Expand Up @@ -225,7 +226,7 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce
this.os = remoteEnv.os;

// this is a copy of what the merged environment collection is on the remote side
await this._resolveEnvironment(backend, variableResolver, shellLaunchConfig);
const env = await this._resolveEnvironment(backend, variableResolver, shellLaunchConfig);

const shouldPersist = !shellLaunchConfig.isFeatureTerminal && this._configHelper.config.enablePersistentSessions && !shellLaunchConfig.disablePersistence;
if (shellLaunchConfig.attachPersistentProcess) {
Expand All @@ -242,13 +243,23 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce
os: this.os
});
try {
const shellIntegration = terminalEnvironment.injectShellIntegrationArgs(this._logService, env, this._configHelper.config.enableShellIntegration, shellLaunchConfig, this.os);
this.shellIntegrationAttempted = shellIntegration.enableShellIntegration;
if (this.shellIntegrationAttempted) {
shellLaunchConfig.args = shellIntegration.args;
// resolve the injected arguments
await this._terminalProfileResolverService.resolveShellLaunchConfig(shellLaunchConfig, {
remoteAuthority: this.remoteAuthority,
os: this.os
});
}
newProcess = await backend.createProcess(
shellLaunchConfig,
'', // TODO: Fix cwd
cols,
rows,
this._configHelper.config.unicodeVersion,
{}, // TODO: Fix env
env,
true, // TODO: Fix enable
shouldPersist
);
Expand Down Expand Up @@ -416,6 +427,16 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce

const env = await this._resolveEnvironment(backend, variableResolver, shellLaunchConfig);

const shellIntegration = terminalEnvironment.injectShellIntegrationArgs(this._logService, env, this._configHelper.config.enableShellIntegration, shellLaunchConfig, OS);
if (shellIntegration.enableShellIntegration) {
shellLaunchConfig.args = shellIntegration.args;
// resolve the injected arguments
await this._terminalProfileResolverService.resolveShellLaunchConfig(shellLaunchConfig, {
remoteAuthority: undefined,
os: OS
});
}
this.shellIntegrationAttempted = shellIntegration.enableShellIntegration;
const useConpty = this._configHelper.config.windowsEnableConpty && !isScreenReaderModeEnabled;
const shouldPersist = this._configHelper.config.enablePersistentSessions && !shellLaunchConfig.isFeatureTerminal;
return await backend.createProcess(shellLaunchConfig, initialCwd, cols, rows, this._configHelper.config.unicodeVersion, env, useConpty, shouldPersist);
Expand Down Expand Up @@ -468,6 +489,18 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce
}));
}

async getBackendOS(): Promise<OperatingSystem> {
let os = OS;
if (!!this.remoteAuthority) {
const remoteEnv = await this._remoteAgentService.getEnvironment();
if (!remoteEnv) {
throw new Error(`Failed to get remote environment for remote authority "${this.remoteAuthority}"`);
}
os = remoteEnv.os;
}
return os;
}

setDimensions(cols: number, rows: number): Promise<void>;
setDimensions(cols: number, rows: number, sync: false): Promise<void>;
setDimensions(cols: number, rows: number, sync: true): void;
Expand Down Expand Up @@ -570,7 +603,7 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce
this._setProcessState(ProcessState.KilledByProcess);
}

this._onProcessExit.fire(exitCode);
this._onProcessExit.fire(this.shellIntegrationAttempted ? 1337 : exitCode);
meganrogge marked this conversation as resolved.
Show resolved Hide resolved
}

private _setProcessState(state: ProcessState) {
Expand Down
1 change: 1 addition & 0 deletions src/vs/workbench/contrib/terminal/common/terminal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,7 @@ export interface ITerminalProcessManager extends IDisposable {
getLatency(): Promise<number>;
refreshProperty<T extends ProcessPropertyType>(type: T): Promise<IProcessPropertyMap[T]>;
updateProperty<T extends ProcessPropertyType>(property: T, value: IProcessPropertyMap[T]): void;
getBackendOS(): Promise<OperatingSystem>;
}

export const enum ProcessState {
Expand Down
16 changes: 10 additions & 6 deletions src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -414,12 +414,12 @@ shellIntegrationArgs.set(ShellIntegrationExecutable.WindowsPwsh, ['-noexit', ' -
shellIntegrationArgs.set(ShellIntegrationExecutable.WindowsPwshLogin, ['-l', '-noexit', ' -command', '. \"${execInstallFolder}\\out\\vs\\workbench\\contrib\\terminal\\browser\\media\\shellIntegration.ps1\"']);
shellIntegrationArgs.set(ShellIntegrationExecutable.Pwsh, ['-noexit', '-command', '. "${execInstallFolder}/out/vs/workbench/contrib/terminal/browser/media/shellIntegration.ps1"']);
shellIntegrationArgs.set(ShellIntegrationExecutable.PwshLogin, ['-l', '-noexit', '-command', '. "${execInstallFolder}/out/vs/workbench/contrib/terminal/browser/media/shellIntegration.ps1"']);
shellIntegrationArgs.set(ShellIntegrationExecutable.Zsh, ['-c', '"${execInstallFolder}/out/vs/workbench/contrib/terminal/browser/media/ShellIntegration-zsh.sh"; zsh -i']);
shellIntegrationArgs.set(ShellIntegrationExecutable.ZshLogin, ['-c', '"${execInstallFolder}/out/vs/workbench/contrib/terminal/browser/media/ShellIntegration-zsh.sh"; zsh -il']);
shellIntegrationArgs.set(ShellIntegrationExecutable.Bash, ['--init-file', '${execInstallFolder}/out/vs/workbench/contrib/terminal/browser/media/ShellIntegration-bash.sh']);
shellIntegrationArgs.set(ShellIntegrationExecutable.Zsh, ['-c', '"${execInstallFolder}/out/vs/workbench/contrib/terminal/browser/media/shellIntegration-zsh.sh"; zsh -i']);
shellIntegrationArgs.set(ShellIntegrationExecutable.ZshLogin, ['-c', '"${execInstallFolder}/out/vs/workbench/contrib/terminal/browser/media/shellIntegration-zsh.sh"; zsh -il']);
shellIntegrationArgs.set(ShellIntegrationExecutable.Bash, ['--init-file', '${execInstallFolder}/out/vs/workbench/contrib/terminal/browser/media/shellIntegration-bash.sh']);
const loginArgs = ['-login', '-l'];
const pwshImpliedArgs = ['-nol', '-nologo'];
export function injectShellIntegrationArgs(logService: ILogService, enableShellIntegration: boolean, shellLaunchConfig: IShellLaunchConfig, isBackendWindows?: boolean): { args: string | string[] | undefined, enableShellIntegration: boolean } {
export function injectShellIntegrationArgs(logService: ILogService, env: IProcessEnvironment, enableShellIntegration: boolean, shellLaunchConfig: IShellLaunchConfig, os?: OperatingSystem): { args: string | string[] | undefined, enableShellIntegration: boolean } {
// Shell integration arg injection is disabled when:
// - The global setting is disabled
// - There is no executable (not sure what script to run)
Expand All @@ -431,7 +431,8 @@ export function injectShellIntegrationArgs(logService: ILogService, enableShellI
const originalArgs = shellLaunchConfig.args;
const shell = path.basename(shellLaunchConfig.executable);
let newArgs: string | string[] | undefined;
if (isBackendWindows) {

if (os === OperatingSystem.Windows) {
if (shell === 'pwsh.exe') {
if (!originalArgs || arePwshImpliedArgs(originalArgs)) {
newArgs = shellIntegrationArgs.get(ShellIntegrationExecutable.WindowsPwsh);
Expand All @@ -444,7 +445,10 @@ export function injectShellIntegrationArgs(logService: ILogService, enableShellI
} else {
switch (shell) {
case 'bash':
if (!originalArgs || originalArgs.length === 0 || areZshBashLoginArgs(originalArgs)) {
if (!originalArgs || originalArgs.length === 0) {
newArgs = shellIntegrationArgs.get(ShellIntegrationExecutable.Bash);
} else if (areZshBashLoginArgs(originalArgs)) {
env['OS_IS_MAC'] = os === OperatingSystem.Macintosh ? '1' : '';
meganrogge marked this conversation as resolved.
Show resolved Hide resolved
newArgs = shellIntegrationArgs.get(ShellIntegrationExecutable.Bash);
}
break;
Expand Down
Loading