update shellType when it changes for local windows terminals (#117998)

Co-authored-by: Daniel Imms <daimms@microsoft.com>
This commit is contained in:
Megan Rogge
2021-03-05 19:04:19 -05:00
committed by GitHub
parent 29b1fcffd1
commit 3e345101df
18 changed files with 145 additions and 159 deletions
@@ -9,6 +9,13 @@ import { IProcessEnvironment } from 'vs/base/common/platform';
import { URI } from 'vs/base/common/uri'; import { URI } from 'vs/base/common/uri';
import { IGetTerminalLayoutInfoArgs, IPtyHostProcessReplayEvent, ISetTerminalLayoutInfoArgs } from 'vs/platform/terminal/common/terminalProcess'; import { IGetTerminalLayoutInfoArgs, IPtyHostProcessReplayEvent, ISetTerminalLayoutInfoArgs } from 'vs/platform/terminal/common/terminalProcess';
export enum WindowsShellType {
CommandPrompt = 'cmd',
PowerShell = 'pwsh',
Wsl = 'wsl',
GitBash = 'gitbash'
}
export type TerminalShellType = WindowsShellType | undefined;
export interface IRawTerminalInstanceLayoutInfo<T> { export interface IRawTerminalInstanceLayoutInfo<T> {
relativeSize: number; relativeSize: number;
terminal: T; terminal: T;
@@ -109,6 +116,7 @@ export interface IPtyService {
readonly onProcessExit: Event<{ id: number, event: number | undefined }>; readonly onProcessExit: Event<{ id: number, event: number | undefined }>;
readonly onProcessReady: Event<{ id: number, event: { pid: number, cwd: string } }>; readonly onProcessReady: Event<{ id: number, event: { pid: number, cwd: string } }>;
readonly onProcessTitleChanged: Event<{ id: number, event: string }>; readonly onProcessTitleChanged: Event<{ id: number, event: string }>;
readonly onProcessShellTypeChanged: Event<{ id: number, event: TerminalShellType }>;
readonly onProcessOverrideDimensions: Event<{ id: number, event: ITerminalDimensionsOverride | undefined }>; readonly onProcessOverrideDimensions: Event<{ id: number, event: ITerminalDimensionsOverride | undefined }>;
readonly onProcessResolvedShellLaunchConfig: Event<{ id: number, event: IShellLaunchConfig }>; readonly onProcessResolvedShellLaunchConfig: Event<{ id: number, event: IShellLaunchConfig }>;
readonly onProcessReplay: Event<{ id: number, event: IPtyHostProcessReplayEvent }>; readonly onProcessReplay: Event<{ id: number, event: IPtyHostProcessReplayEvent }>;
@@ -296,6 +304,7 @@ export interface ITerminalChildProcess {
onProcessTitleChanged: Event<string>; onProcessTitleChanged: Event<string>;
onProcessOverrideDimensions?: Event<ITerminalDimensionsOverride | undefined>; onProcessOverrideDimensions?: Event<ITerminalDimensionsOverride | undefined>;
onProcessResolvedShellLaunchConfig?: Event<IShellLaunchConfig>; onProcessResolvedShellLaunchConfig?: Event<IShellLaunchConfig>;
onProcessShellTypeChanged: Event<TerminalShellType>;
/** /**
* Starts the process. * Starts the process.
@@ -5,7 +5,7 @@
import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { Disposable, toDisposable } from 'vs/base/common/lifecycle';
import { ILogService } from 'vs/platform/log/common/log'; import { ILogService } from 'vs/platform/log/common/log';
import { IPtyService, IProcessDataEvent, IShellLaunchConfig, ITerminalDimensionsOverride, ITerminalLaunchError, ITerminalsLayoutInfo, TerminalIpcChannels, IHeartbeatService, HeartbeatConstants } from 'vs/platform/terminal/common/terminal'; import { IPtyService, IProcessDataEvent, IShellLaunchConfig, ITerminalDimensionsOverride, ITerminalLaunchError, ITerminalsLayoutInfo, TerminalIpcChannels, IHeartbeatService, HeartbeatConstants, TerminalShellType } from 'vs/platform/terminal/common/terminal';
import { Client } from 'vs/base/parts/ipc/node/ipc.cp'; import { Client } from 'vs/base/parts/ipc/node/ipc.cp';
import { FileAccess } from 'vs/base/common/network'; import { FileAccess } from 'vs/base/common/network';
import { ProxyChannel } from 'vs/base/parts/ipc/common/ipc'; import { ProxyChannel } from 'vs/base/parts/ipc/common/ipc';
@@ -56,6 +56,8 @@ export class LocalPtyService extends Disposable implements IPtyService {
readonly onProcessReplay = this._onProcessReplay.event; readonly onProcessReplay = this._onProcessReplay.event;
private readonly _onProcessTitleChanged = this._register(new Emitter<{ id: number, event: string }>()); private readonly _onProcessTitleChanged = this._register(new Emitter<{ id: number, event: string }>());
readonly onProcessTitleChanged = this._onProcessTitleChanged.event; readonly onProcessTitleChanged = this._onProcessTitleChanged.event;
private readonly _onProcessShellTypeChanged = this._register(new Emitter<{ id: number, event: TerminalShellType }>());
readonly onProcessShellTypeChanged = this._onProcessShellTypeChanged.event;
private readonly _onProcessOverrideDimensions = this._register(new Emitter<{ id: number, event: ITerminalDimensionsOverride | undefined }>()); private readonly _onProcessOverrideDimensions = this._register(new Emitter<{ id: number, event: ITerminalDimensionsOverride | undefined }>());
readonly onProcessOverrideDimensions = this._onProcessOverrideDimensions.event; readonly onProcessOverrideDimensions = this._onProcessOverrideDimensions.event;
private readonly _onProcessResolvedShellLaunchConfig = this._register(new Emitter<{ id: number, event: IShellLaunchConfig }>()); private readonly _onProcessResolvedShellLaunchConfig = this._register(new Emitter<{ id: number, event: IShellLaunchConfig }>());
@@ -116,6 +118,7 @@ export class LocalPtyService extends Disposable implements IPtyService {
this._register(proxy.onProcessExit(e => this._onProcessExit.fire(e))); this._register(proxy.onProcessExit(e => this._onProcessExit.fire(e)));
this._register(proxy.onProcessReady(e => this._onProcessReady.fire(e))); this._register(proxy.onProcessReady(e => this._onProcessReady.fire(e)));
this._register(proxy.onProcessTitleChanged(e => this._onProcessTitleChanged.fire(e))); this._register(proxy.onProcessTitleChanged(e => this._onProcessTitleChanged.fire(e)));
this._register(proxy.onProcessShellTypeChanged(e => this._onProcessShellTypeChanged.fire(e)));
this._register(proxy.onProcessOverrideDimensions(e => this._onProcessOverrideDimensions.fire(e))); this._register(proxy.onProcessOverrideDimensions(e => this._onProcessOverrideDimensions.fire(e)));
this._register(proxy.onProcessResolvedShellLaunchConfig(e => this._onProcessResolvedShellLaunchConfig.fire(e))); this._register(proxy.onProcessResolvedShellLaunchConfig(e => this._onProcessResolvedShellLaunchConfig.fire(e)));
this._register(proxy.onProcessReplay(e => this._onProcessReplay.fire(e))); this._register(proxy.onProcessReplay(e => this._onProcessReplay.fire(e)));
+8 -2
View File
@@ -5,7 +5,7 @@
import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { Disposable, toDisposable } from 'vs/base/common/lifecycle';
import { IProcessEnvironment } from 'vs/base/common/platform'; import { IProcessEnvironment } from 'vs/base/common/platform';
import { IPtyService, IProcessDataEvent, IShellLaunchConfig, ITerminalDimensionsOverride, ITerminalLaunchError, LocalReconnectConstants, ITerminalsLayoutInfo, IRawTerminalInstanceLayoutInfo, ITerminalTabLayoutInfoById, ITerminalInstanceLayoutInfoById } from 'vs/platform/terminal/common/terminal'; import { IPtyService, IProcessDataEvent, IShellLaunchConfig, ITerminalDimensionsOverride, ITerminalLaunchError, LocalReconnectConstants, ITerminalsLayoutInfo, IRawTerminalInstanceLayoutInfo, ITerminalTabLayoutInfoById, ITerminalInstanceLayoutInfoById, TerminalShellType } from 'vs/platform/terminal/common/terminal';
import { AutoOpenBarrier, Queue, RunOnceScheduler } from 'vs/base/common/async'; import { AutoOpenBarrier, Queue, RunOnceScheduler } from 'vs/base/common/async';
import { Emitter } from 'vs/base/common/event'; import { Emitter } from 'vs/base/common/event';
import { TerminalRecorder } from 'vs/platform/terminal/common/terminalRecorder'; import { TerminalRecorder } from 'vs/platform/terminal/common/terminalRecorder';
@@ -34,6 +34,8 @@ export class PtyService extends Disposable implements IPtyService {
readonly onProcessReady = this._onProcessReady.event; readonly onProcessReady = this._onProcessReady.event;
private readonly _onProcessTitleChanged = this._register(new Emitter<{ id: number, event: string }>()); private readonly _onProcessTitleChanged = this._register(new Emitter<{ id: number, event: string }>());
readonly onProcessTitleChanged = this._onProcessTitleChanged.event; readonly onProcessTitleChanged = this._onProcessTitleChanged.event;
private readonly _onProcessShellTypeChanged = this._register(new Emitter<{ id: number, event: TerminalShellType }>());
readonly onProcessShellTypeChanged = this._onProcessShellTypeChanged.event;
private readonly _onProcessOverrideDimensions = this._register(new Emitter<{ id: number, event: ITerminalDimensionsOverride | undefined }>()); private readonly _onProcessOverrideDimensions = this._register(new Emitter<{ id: number, event: ITerminalDimensionsOverride | undefined }>());
readonly onProcessOverrideDimensions = this._onProcessOverrideDimensions.event; readonly onProcessOverrideDimensions = this._onProcessOverrideDimensions.event;
private readonly _onProcessResolvedShellLaunchConfig = this._register(new Emitter<{ id: number, event: IShellLaunchConfig }>()); private readonly _onProcessResolvedShellLaunchConfig = this._register(new Emitter<{ id: number, event: IShellLaunchConfig }>());
@@ -90,6 +92,7 @@ export class PtyService extends Disposable implements IPtyService {
persistentProcess.onProcessReplay(event => this._onProcessReplay.fire({ id, event })); persistentProcess.onProcessReplay(event => this._onProcessReplay.fire({ id, event }));
persistentProcess.onProcessReady(event => this._onProcessReady.fire({ id, event })); persistentProcess.onProcessReady(event => this._onProcessReady.fire({ id, event }));
persistentProcess.onProcessTitleChanged(event => this._onProcessTitleChanged.fire({ id, event })); persistentProcess.onProcessTitleChanged(event => this._onProcessTitleChanged.fire({ id, event }));
persistentProcess.onProcessShellTypeChanged(event => this._onProcessShellTypeChanged.fire({ id, event }));
this._ptys.set(id, persistentProcess); this._ptys.set(id, persistentProcess);
return id; return id;
} }
@@ -219,6 +222,8 @@ export class PersistentTerminalProcess extends Disposable {
readonly onProcessReady = this._onProcessReady.event; readonly onProcessReady = this._onProcessReady.event;
private readonly _onProcessTitleChanged = this._register(new Emitter<string>()); private readonly _onProcessTitleChanged = this._register(new Emitter<string>());
readonly onProcessTitleChanged = this._onProcessTitleChanged.event; readonly onProcessTitleChanged = this._onProcessTitleChanged.event;
private readonly _onProcessShellTypeChanged = this._register(new Emitter<TerminalShellType>());
readonly onProcessShellTypeChanged = this._onProcessShellTypeChanged.event;
private readonly _onProcessOverrideDimensions = this._register(new Emitter<ITerminalDimensionsOverride | undefined>()); private readonly _onProcessOverrideDimensions = this._register(new Emitter<ITerminalDimensionsOverride | undefined>());
readonly onProcessOverrideDimensions = this._onProcessOverrideDimensions.event; readonly onProcessOverrideDimensions = this._onProcessOverrideDimensions.event;
private readonly _onProcessData = this._register(new Emitter<IProcessDataEvent>()); private readonly _onProcessData = this._register(new Emitter<IProcessDataEvent>());
@@ -269,7 +274,7 @@ export class PersistentTerminalProcess extends Disposable {
this._onProcessReady.fire(e); this._onProcessReady.fire(e);
})); }));
this._register(this._terminalProcess.onProcessTitleChanged(e => this._onProcessTitleChanged.fire(e))); this._register(this._terminalProcess.onProcessTitleChanged(e => this._onProcessTitleChanged.fire(e)));
this._register(this._terminalProcess.onProcessShellTypeChanged(e => this._onProcessShellTypeChanged.fire(e)));
// Buffer data events to reduce the amount of messages going to the renderer // Buffer data events to reduce the amount of messages going to the renderer
// this._register(this._bufferer.startBuffering(this._persistentProcessId, this._terminalProcess.onProcessData)); // this._register(this._bufferer.startBuffering(this._persistentProcessId, this._terminalProcess.onProcessData));
this._register(this._terminalProcess.onProcessData(e => this._recorder.recordData(e))); this._register(this._terminalProcess.onProcessData(e => this._recorder.recordData(e)));
@@ -301,6 +306,7 @@ export class PersistentTerminalProcess extends Disposable {
} else { } else {
this._onProcessReady.fire({ pid: this._pid, cwd: this._cwd }); this._onProcessReady.fire({ pid: this._pid, cwd: this._cwd });
this._onProcessTitleChanged.fire(this._terminalProcess.currentTitle); this._onProcessTitleChanged.fire(this._terminalProcess.currentTitle);
this._onProcessShellTypeChanged.fire(this._terminalProcess.shellType);
this.triggerReplay(); this.triggerReplay();
} }
return undefined; return undefined;
@@ -10,12 +10,13 @@ import * as fs from 'fs';
import * as os from 'os'; import * as os from 'os';
import { Event, Emitter } from 'vs/base/common/event'; import { Event, Emitter } from 'vs/base/common/event';
import { Disposable } from 'vs/base/common/lifecycle'; import { Disposable } from 'vs/base/common/lifecycle';
import { IShellLaunchConfig, ITerminalLaunchError, FlowControlConstants, ITerminalChildProcess, ITerminalDimensionsOverride } from 'vs/platform/terminal/common/terminal'; import { IShellLaunchConfig, ITerminalLaunchError, FlowControlConstants, ITerminalChildProcess, ITerminalDimensionsOverride, TerminalShellType } from 'vs/platform/terminal/common/terminal';
import { exec } from 'child_process'; import { exec } from 'child_process';
import { ILogService } from 'vs/platform/log/common/log'; import { ILogService } from 'vs/platform/log/common/log';
import { findExecutable, getWindowsBuildNumber } from 'vs/platform/terminal/node/terminalEnvironment'; import { findExecutable, getWindowsBuildNumber } from 'vs/platform/terminal/node/terminalEnvironment';
import { URI } from 'vs/base/common/uri'; import { URI } from 'vs/base/common/uri';
import { localize } from 'vs/nls'; import { localize } from 'vs/nls';
import { WindowsShellHelper } from 'vs/platform/terminal/node/windowsShellHelper';
// Writing large amounts of data can be corrupted for some reason, after looking into this is // Writing large amounts of data can be corrupted for some reason, after looking into this is
// appears to be a race condition around writing to the FD which may be based on how powerful the // appears to be a race condition around writing to the FD which may be based on how powerful the
@@ -54,6 +55,7 @@ export class TerminalProcess extends Disposable implements ITerminalChildProcess
private _currentTitle: string = ''; private _currentTitle: string = '';
private _processStartupComplete: Promise<void> | undefined; private _processStartupComplete: Promise<void> | undefined;
private _isDisposed: boolean = false; private _isDisposed: boolean = false;
private _windowsShellHelper: WindowsShellHelper | undefined;
private _titleInterval: NodeJS.Timer | null = null; private _titleInterval: NodeJS.Timer | null = null;
private _writeQueue: string[] = []; private _writeQueue: string[] = [];
private _writeTimeout: NodeJS.Timeout | undefined; private _writeTimeout: NodeJS.Timeout | undefined;
@@ -63,9 +65,10 @@ export class TerminalProcess extends Disposable implements ITerminalChildProcess
private _isPtyPaused: boolean = false; private _isPtyPaused: boolean = false;
private _unacknowledgedCharCount: number = 0; private _unacknowledgedCharCount: number = 0;
public get exitMessage(): string | undefined { return this._exitMessage; } public get exitMessage(): string | undefined { return this._exitMessage; }
public get currentTitle(): string { return this._currentTitle; }
public get currentTitle(): string { return this._windowsShellHelper?.shellTitle || this._currentTitle; }
public get shellType(): TerminalShellType { return this._windowsShellHelper ? this._windowsShellHelper.shellType : undefined; }
private readonly _onProcessData = this._register(new Emitter<string>()); private readonly _onProcessData = this._register(new Emitter<string>());
public get onProcessData(): Event<string> { return this._onProcessData.event; } public get onProcessData(): Event<string> { return this._onProcessData.event; }
@@ -75,6 +78,8 @@ export class TerminalProcess extends Disposable implements ITerminalChildProcess
public get onProcessReady(): Event<{ pid: number, cwd: string }> { return this._onProcessReady.event; } public get onProcessReady(): Event<{ pid: number, cwd: string }> { return this._onProcessReady.event; }
private readonly _onProcessTitleChanged = this._register(new Emitter<string>()); private readonly _onProcessTitleChanged = this._register(new Emitter<string>());
public get onProcessTitleChanged(): Event<string> { return this._onProcessTitleChanged.event; } public get onProcessTitleChanged(): Event<string> { return this._onProcessTitleChanged.event; }
private readonly _onProcessShellTypeChanged = this._register(new Emitter<TerminalShellType>());
public readonly onProcessShellTypeChanged = this._onProcessShellTypeChanged.event;
constructor( constructor(
private readonly _shellLaunchConfig: IShellLaunchConfig, private readonly _shellLaunchConfig: IShellLaunchConfig,
@@ -111,15 +116,23 @@ export class TerminalProcess extends Disposable implements ITerminalChildProcess
conptyInheritCursor: useConpty && !!_shellLaunchConfig.initialText conptyInheritCursor: useConpty && !!_shellLaunchConfig.initialText
}; };
// Delay resizes to avoid conpty not respecting very early resize calls // Delay resizes to avoid conpty not respecting very early resize calls
if (platform.isWindows && useConpty && cols === 0 && rows === 0 && this._shellLaunchConfig.executable?.endsWith('Git\\bin\\bash.exe')) { if (platform.isWindows) {
this._delayedResizer = new DelayedResizer(); if (useConpty && cols === 0 && rows === 0 && this._shellLaunchConfig.executable?.endsWith('Git\\bin\\bash.exe')) {
this._register(this._delayedResizer.onTrigger(dimensions => { this._delayedResizer = new DelayedResizer();
this._delayedResizer?.dispose(); this._register(this._delayedResizer.onTrigger(dimensions => {
this._delayedResizer = undefined; this._delayedResizer?.dispose();
if (dimensions.cols && dimensions.rows) { this._delayedResizer = undefined;
this.resize(dimensions.cols, dimensions.rows); if (dimensions.cols && dimensions.rows) {
} this.resize(dimensions.cols, dimensions.rows);
})); }
}));
}
// WindowsShellHelper is used to fetch the process title and shell type
this.onProcessReady(e => {
this._windowsShellHelper = this._register(new WindowsShellHelper(e.pid));
this._register(this._windowsShellHelper.onShellTypeChanged(e => this._onProcessShellTypeChanged.fire(e)));
this._register(this._windowsShellHelper.onShellNameChanged(e => this._onProcessTitleChanged.fire(e)));
});
} }
} }
onProcessOverrideDimensions?: Event<ITerminalDimensionsOverride | undefined> | undefined; onProcessOverrideDimensions?: Event<ITerminalDimensionsOverride | undefined> | undefined;
@@ -196,12 +209,14 @@ export class TerminalProcess extends Disposable implements ITerminalChildProcess
ptyProcess.pause(); ptyProcess.pause();
} }
// Refire the data event // Refire the data event
this._onProcessData.fire(data); this._onProcessData.fire(data);
if (this._closeTimeout) { if (this._closeTimeout) {
clearTimeout(this._closeTimeout); clearTimeout(this._closeTimeout);
this._queueProcessExit(); this._queueProcessExit();
} }
this._windowsShellHelper?.checkShell();
}); });
ptyProcess.onExit(e => { ptyProcess.onExit(e => {
this._exitCode = e.exitCode; this._exitCode = e.exitCode;
@@ -217,10 +232,6 @@ export class TerminalProcess extends Disposable implements ITerminalChildProcess
clearInterval(this._titleInterval); clearInterval(this._titleInterval);
} }
this._titleInterval = null; this._titleInterval = null;
this._onProcessData.dispose();
this._onProcessExit.dispose();
this._onProcessReady.dispose();
this._onProcessTitleChanged.dispose();
super.dispose(); super.dispose();
} }
@@ -5,12 +5,19 @@
import * as platform from 'vs/base/common/platform'; import * as platform from 'vs/base/common/platform';
import { Emitter, Event } from 'vs/base/common/event'; import { Emitter, Event } from 'vs/base/common/event';
import { IWindowsShellHelper } from 'vs/workbench/contrib/terminal/common/terminal';
import type { Terminal as XTermTerminal } from 'xterm';
import type * as WindowsProcessTreeType from 'windows-process-tree'; import type * as WindowsProcessTreeType from 'windows-process-tree';
import { Disposable } from 'vs/base/common/lifecycle'; import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
import { TerminalShellType, WindowsShellType } from 'vs/platform/terminal/common/terminal';
import { debounce } from 'vs/base/common/decorators';
import { timeout } from 'vs/base/common/async'; import { timeout } from 'vs/base/common/async';
export interface IWindowsShellHelper extends IDisposable {
readonly onShellNameChanged: Event<string>;
readonly onShellTypeChanged: Event<TerminalShellType>;
getShellType(title: string): TerminalShellType;
getShellName(): Promise<string>;
}
const SHELL_EXECUTABLES = [ const SHELL_EXECUTABLES = [
'cmd.exe', 'cmd.exe',
'powershell.exe', 'powershell.exe',
@@ -28,17 +35,19 @@ const SHELL_EXECUTABLES = [
let windowsProcessTree: typeof WindowsProcessTreeType; let windowsProcessTree: typeof WindowsProcessTreeType;
export class WindowsShellHelper extends Disposable implements IWindowsShellHelper { export class WindowsShellHelper extends Disposable implements IWindowsShellHelper {
private _onCheckShell: Emitter<Promise<string> | undefined> = this._register(new Emitter<Promise<string> | undefined>());
private _isDisposed: boolean; private _isDisposed: boolean;
private _currentRequest: Promise<string> | undefined; private _currentRequest: Promise<string> | undefined;
private _newLineFeed: boolean = false; private _shellType: TerminalShellType | undefined;
public get shellType(): TerminalShellType | undefined { return this._shellType; }
private readonly _onShellNameChange = new Emitter<string>(); private _shellTitle: string = '';
public get onShellNameChange(): Event<string> { return this._onShellNameChange.event; } public get shellTitle(): string { return this._shellTitle; }
private readonly _onShellNameChanged = new Emitter<string>();
public get onShellNameChanged(): Event<string> { return this._onShellNameChanged.event; }
private readonly _onShellTypeChanged = new Emitter<TerminalShellType>();
public get onShellTypeChanged(): Event<TerminalShellType> { return this._onShellTypeChanged.event; }
public constructor( public constructor(
private _rootProcessId: number, private _rootProcessId: number
private _xterm: XTermTerminal
) { ) {
super(); super();
@@ -55,35 +64,25 @@ export class WindowsShellHelper extends Disposable implements IWindowsShellHelpe
if (this._isDisposed) { if (this._isDisposed) {
return; return;
} }
this.checkShell();
// The debounce is necessary to prevent multiple processes from spawning when
// the enter key or output is spammed
Event.debounce(this._onCheckShell.event, (l, e) => e, 150, true)(async () => {
await timeout(50);
this.checkShell();
});
// We want to fire a new check for the shell on a linefeed, but only
// when parsing has finished which is indicated by the cursormove event.
// If this is done on every linefeed, parsing ends up taking
// significantly longer due to resetting timers. Note that this is
// private API.
this._xterm.onLineFeed(() => this._newLineFeed = true);
this._xterm.onCursorMove(() => {
if (this._newLineFeed) {
this._onCheckShell.fire(undefined);
this._newLineFeed = false;
}
});
// Fire a new check for the shell when any key is pressed.
this._xterm.onKey(() => this._onCheckShell.fire(undefined));
} }
private checkShell(): void { @debounce(500)
async checkShell(): Promise<void> {
if (platform.isWindows) { if (platform.isWindows) {
// TODO: Only fire when it's different // Wait to give the shell some time to actually launch a process, this
this.getShellName().then(title => this._onShellNameChange.fire(title)); // could lead to a race condition but it would be recovered from when
// data stops and should cover the majority of cases
await timeout(300);
this.getShellName().then(title => {
const type = this.getShellType(title);
if (type !== this._shellType) {
this._onShellTypeChanged.fire(type);
this._onShellNameChanged.fire(title);
this._shellType = type;
this._shellTitle = title;
}
});
} }
} }
@@ -141,4 +140,27 @@ export class WindowsShellHelper extends Disposable implements IWindowsShellHelpe
}); });
return this._currentRequest; return this._currentRequest;
} }
public getShellType(executable: string): TerminalShellType {
switch (executable.toLowerCase()) {
case 'cmd.exe':
return WindowsShellType.CommandPrompt;
case 'powershell.exe':
case 'pwsh.exe':
return WindowsShellType.PowerShell;
case 'bash.exe':
case 'git-cmd.exe':
return WindowsShellType.GitBash;
case 'wsl.exe':
case 'ubuntu.exe':
case 'ubuntu1804.exe':
case 'kali.exe':
case 'debian.exe':
case 'opensuse-42.exe':
case 'sles-12.exe':
return WindowsShellType.Wsl;
default:
return undefined;
}
}
} }
@@ -19,7 +19,7 @@ import { serializeEnvironmentVariableCollection } from 'vs/workbench/contrib/ter
import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { CancellationTokenSource } from 'vs/base/common/cancellation';
import { generateUuid } from 'vs/base/common/uuid'; import { generateUuid } from 'vs/base/common/uuid';
import { ISerializableEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariable'; import { ISerializableEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariable';
import { ITerminalChildProcess, ITerminalDimensionsOverride, ITerminalLaunchError } from 'vs/platform/terminal/common/terminal'; import { ITerminalChildProcess, ITerminalDimensionsOverride, ITerminalLaunchError, TerminalShellType } from 'vs/platform/terminal/common/terminal';
import { TerminalDataBufferer } from 'vs/platform/terminal/common/terminalDataBuffering'; import { TerminalDataBufferer } from 'vs/platform/terminal/common/terminalDataBuffering';
export interface IExtHostTerminalService extends ExtHostTerminalServiceShape, IDisposable { export interface IExtHostTerminalService extends ExtHostTerminalServiceShape, IDisposable {
@@ -198,6 +198,9 @@ export class ExtHostPseudoterminal implements ITerminalChildProcess {
public readonly onProcessTitleChanged: Event<string> = this._onProcessTitleChanged.event; public readonly onProcessTitleChanged: Event<string> = this._onProcessTitleChanged.event;
private readonly _onProcessOverrideDimensions = new Emitter<ITerminalDimensionsOverride | undefined>(); private readonly _onProcessOverrideDimensions = new Emitter<ITerminalDimensionsOverride | undefined>();
public get onProcessOverrideDimensions(): Event<ITerminalDimensionsOverride | undefined> { return this._onProcessOverrideDimensions.event; } public get onProcessOverrideDimensions(): Event<ITerminalDimensionsOverride | undefined> { return this._onProcessOverrideDimensions.event; }
private readonly _onProcessShellTypeChanged = new Emitter<TerminalShellType>();
public readonly onProcessShellTypeChanged = this._onProcessShellTypeChanged.event;
constructor(private readonly _pty: vscode.Pseudoterminal) { } constructor(private readonly _pty: vscode.Pseudoterminal) { }
@@ -16,7 +16,7 @@ import { IRemoteTerminalService, ITerminalInstanceService } from 'vs/workbench/c
import { IRemoteTerminalProcessExecCommandEvent, IShellLaunchConfigDto, RemoteTerminalChannelClient, REMOTE_TERMINAL_CHANNEL_NAME } from 'vs/workbench/contrib/terminal/common/remoteTerminalChannel'; import { IRemoteTerminalProcessExecCommandEvent, IShellLaunchConfigDto, RemoteTerminalChannelClient, REMOTE_TERMINAL_CHANNEL_NAME } from 'vs/workbench/contrib/terminal/common/remoteTerminalChannel';
import { IRemoteTerminalAttachTarget, ITerminalConfigHelper } from 'vs/workbench/contrib/terminal/common/terminal'; import { IRemoteTerminalAttachTarget, ITerminalConfigHelper } from 'vs/workbench/contrib/terminal/common/terminal';
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
import { IProcessDataEvent, IShellLaunchConfig, ITerminalChildProcess, ITerminalDimensionsOverride, ITerminalLaunchError, ITerminalsLayoutInfo, ITerminalsLayoutInfoById } from 'vs/platform/terminal/common/terminal'; import { IProcessDataEvent, IShellLaunchConfig, ITerminalChildProcess, ITerminalDimensionsOverride, ITerminalLaunchError, ITerminalsLayoutInfo, ITerminalsLayoutInfoById, TerminalShellType } from 'vs/platform/terminal/common/terminal';
export class RemoteTerminalService extends Disposable implements IRemoteTerminalService { export class RemoteTerminalService extends Disposable implements IRemoteTerminalService {
public _serviceBrand: undefined; public _serviceBrand: undefined;
@@ -101,6 +101,8 @@ export class RemoteTerminalProcess extends Disposable implements ITerminalChildP
public readonly onProcessOverrideDimensions: Event<ITerminalDimensionsOverride | undefined> = this._onProcessOverrideDimensions.event; public readonly onProcessOverrideDimensions: Event<ITerminalDimensionsOverride | undefined> = this._onProcessOverrideDimensions.event;
private readonly _onProcessResolvedShellLaunchConfig = this._register(new Emitter<IShellLaunchConfig>()); private readonly _onProcessResolvedShellLaunchConfig = this._register(new Emitter<IShellLaunchConfig>());
public get onProcessResolvedShellLaunchConfig(): Event<IShellLaunchConfig> { return this._onProcessResolvedShellLaunchConfig.event; } public get onProcessResolvedShellLaunchConfig(): Event<IShellLaunchConfig> { return this._onProcessResolvedShellLaunchConfig.event; }
private readonly _onProcessShellTypeChanged = this._register(new Emitter<TerminalShellType | undefined>());
public readonly onProcessShellTypeChanged = this._onProcessShellTypeChanged.event;
private _startBarrier: Barrier; private _startBarrier: Barrier;
private _persistentProcessId: number; private _persistentProcessId: number;
@@ -26,7 +26,7 @@ import { setupTerminalMenu } from 'vs/workbench/contrib/terminal/common/terminal
import { IConfigurationRegistry, Extensions } from 'vs/platform/configuration/common/configurationRegistry'; import { IConfigurationRegistry, Extensions } from 'vs/platform/configuration/common/configurationRegistry';
import { TerminalService } from 'vs/workbench/contrib/terminal/browser/terminalService'; import { TerminalService } from 'vs/workbench/contrib/terminal/browser/terminalService';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { IRemoteTerminalService, ITerminalService, WindowsShellType } from 'vs/workbench/contrib/terminal/browser/terminal'; import { IRemoteTerminalService, ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal';
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
import { ViewPaneContainer } from 'vs/workbench/browser/parts/views/viewPaneContainer'; import { ViewPaneContainer } from 'vs/workbench/browser/parts/views/viewPaneContainer';
import { IQuickAccessRegistry, Extensions as QuickAccessExtensions } from 'vs/platform/quickinput/common/quickAccess'; import { IQuickAccessRegistry, Extensions as QuickAccessExtensions } from 'vs/platform/quickinput/common/quickAccess';
@@ -36,6 +36,7 @@ import { CONTEXT_ACCESSIBILITY_MODE_ENABLED } from 'vs/platform/accessibility/co
import { terminalViewIcon } from 'vs/workbench/contrib/terminal/browser/terminalIcons'; import { terminalViewIcon } from 'vs/workbench/contrib/terminal/browser/terminalIcons';
import { RemoteTerminalService } from 'vs/workbench/contrib/terminal/browser/remoteTerminalService'; import { RemoteTerminalService } from 'vs/workbench/contrib/terminal/browser/remoteTerminalService';
import { isIPad } from 'vs/base/browser/browser'; import { isIPad } from 'vs/base/browser/browser';
import { WindowsShellType } from 'vs/platform/terminal/common/terminal';
// Register services // Register services
registerSingleton(ITerminalService, TerminalService, true); registerSingleton(ITerminalService, TerminalService, true);
@@ -9,8 +9,8 @@ import { IProcessEnvironment, Platform } from 'vs/base/common/platform';
import { URI } from 'vs/base/common/uri'; import { URI } from 'vs/base/common/uri';
import { FindReplaceState } from 'vs/editor/contrib/find/findState'; import { FindReplaceState } from 'vs/editor/contrib/find/findState';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IShellLaunchConfig, ITerminalChildProcess, ITerminalDimensions, ITerminalLaunchError, ITerminalsLayoutInfo, ITerminalsLayoutInfoById, ITerminalTabLayoutInfoById } from 'vs/platform/terminal/common/terminal'; import { IShellLaunchConfig, ITerminalChildProcess, ITerminalDimensions, ITerminalLaunchError, ITerminalsLayoutInfo, ITerminalsLayoutInfoById, ITerminalTabLayoutInfoById, TerminalShellType } from 'vs/platform/terminal/common/terminal';
import { IAvailableShellsRequest, ICommandTracker, IDefaultShellAndArgsRequest, INavigationMode, IRemoteTerminalAttachTarget, IStartExtensionTerminalRequest, ITerminalConfigHelper, ITerminalNativeWindowsDelegate, ITerminalProcessExtHostProxy, IWindowsShellHelper, LinuxDistro, TitleEventSource } from 'vs/workbench/contrib/terminal/common/terminal'; import { IAvailableShellsRequest, ICommandTracker, IDefaultShellAndArgsRequest, INavigationMode, IRemoteTerminalAttachTarget, IStartExtensionTerminalRequest, ITerminalConfigHelper, ITerminalNativeWindowsDelegate, ITerminalProcessExtHostProxy, LinuxDistro, TitleEventSource } from 'vs/workbench/contrib/terminal/common/terminal';
import type { Terminal as XTermTerminal } from 'xterm'; import type { Terminal as XTermTerminal } from 'xterm';
import type { SearchAddon as XTermSearchAddon } from 'xterm-addon-search'; import type { SearchAddon as XTermSearchAddon } from 'xterm-addon-search';
import type { Unicode11Addon as XTermUnicode11Addon } from 'xterm-addon-unicode11'; import type { Unicode11Addon as XTermUnicode11Addon } from 'xterm-addon-unicode11';
@@ -35,7 +35,6 @@ export interface ITerminalInstanceService {
getXtermSearchConstructor(): Promise<typeof XTermSearchAddon>; getXtermSearchConstructor(): Promise<typeof XTermSearchAddon>;
getXtermUnicode11Constructor(): Promise<typeof XTermUnicode11Addon>; getXtermUnicode11Constructor(): Promise<typeof XTermUnicode11Addon>;
getXtermWebglConstructor(): Promise<typeof XTermWebglAddon>; getXtermWebglConstructor(): Promise<typeof XTermWebglAddon>;
createWindowsShellHelper(shellProcessId: number, xterm: XTermTerminal): IWindowsShellHelper;
getDefaultShellAndArgs(useAutomationShell: boolean, platformOverride?: Platform): Promise<{ shell: string, args: string[] | string | undefined }>; getDefaultShellAndArgs(useAutomationShell: boolean, platformOverride?: Platform): Promise<{ shell: string, args: string[] | string | undefined }>;
getMainProcessParentEnv(): Promise<IProcessEnvironment>; getMainProcessParentEnv(): Promise<IProcessEnvironment>;
} }
@@ -226,14 +225,6 @@ export interface ISearchOptions {
incremental?: boolean; incremental?: boolean;
} }
export enum WindowsShellType {
CommandPrompt = 'cmd',
PowerShell = 'pwsh',
Wsl = 'wsl',
GitBash = 'gitbash'
}
export type TerminalShellType = WindowsShellType | undefined;
export interface ITerminalBeforeHandleLinkEvent { export interface ITerminalBeforeHandleLinkEvent {
terminal?: ITerminalInstance; terminal?: ITerminalInstance;
/** The text of the link */ /** The text of the link */
@@ -536,11 +527,6 @@ export interface ITerminalInstance {
*/ */
setTitle(title: string, eventSource: TitleEventSource): void; setTitle(title: string, eventSource: TitleEventSource): void;
/**
* Sets the shell type of the terminal instance.
*/
setShellType(shellType: TerminalShellType): void;
waitForTitle(): Promise<string>; waitForTitle(): Promise<string>;
setDimensions(dimensions: ITerminalDimensions): void; setDimensions(dimensions: ITerminalDimensions): void;
@@ -25,12 +25,12 @@ import { activeContrastBorder, scrollbarSliderActiveBackground, scrollbarSliderB
import { ICssStyleCollector, IColorTheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { ICssStyleCollector, IColorTheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
import { PANEL_BACKGROUND, SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; import { PANEL_BACKGROUND, SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme';
import { TerminalWidgetManager } from 'vs/workbench/contrib/terminal/browser/widgets/widgetManager'; import { TerminalWidgetManager } from 'vs/workbench/contrib/terminal/browser/widgets/widgetManager';
import { ITerminalProcessManager, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, NEVER_MEASURE_RENDER_TIME_STORAGE_KEY, ProcessState, TERMINAL_VIEW_ID, IWindowsShellHelper, KEYBINDING_CONTEXT_TERMINAL_A11Y_TREE_FOCUS, INavigationMode, TitleEventSource, DEFAULT_COMMANDS_TO_SKIP_SHELL, TERMINAL_CREATION_COMMANDS, KEYBINDING_CONTEXT_TERMINAL_ALT_BUFFER_ACTIVE, SUGGESTED_RENDERER_TYPE } from 'vs/workbench/contrib/terminal/common/terminal'; import { ITerminalProcessManager, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, NEVER_MEASURE_RENDER_TIME_STORAGE_KEY, ProcessState, TERMINAL_VIEW_ID, KEYBINDING_CONTEXT_TERMINAL_A11Y_TREE_FOCUS, INavigationMode, TitleEventSource, DEFAULT_COMMANDS_TO_SKIP_SHELL, TERMINAL_CREATION_COMMANDS, KEYBINDING_CONTEXT_TERMINAL_ALT_BUFFER_ACTIVE, SUGGESTED_RENDERER_TYPE } from 'vs/workbench/contrib/terminal/common/terminal';
import { ansiColorIdentifiers, TERMINAL_BACKGROUND_COLOR, TERMINAL_CURSOR_BACKGROUND_COLOR, TERMINAL_CURSOR_FOREGROUND_COLOR, TERMINAL_FOREGROUND_COLOR, TERMINAL_SELECTION_BACKGROUND_COLOR } from 'vs/workbench/contrib/terminal/common/terminalColorRegistry'; import { ansiColorIdentifiers, TERMINAL_BACKGROUND_COLOR, TERMINAL_CURSOR_BACKGROUND_COLOR, TERMINAL_CURSOR_FOREGROUND_COLOR, TERMINAL_FOREGROUND_COLOR, TERMINAL_SELECTION_BACKGROUND_COLOR } from 'vs/workbench/contrib/terminal/common/terminalColorRegistry';
import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminalConfigHelper'; import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminalConfigHelper';
import { TerminalLinkManager } from 'vs/workbench/contrib/terminal/browser/links/terminalLinkManager'; import { TerminalLinkManager } from 'vs/workbench/contrib/terminal/browser/links/terminalLinkManager';
import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility';
import { ITerminalInstanceService, ITerminalInstance, TerminalShellType, WindowsShellType, ITerminalExternalLinkProvider } from 'vs/workbench/contrib/terminal/browser/terminal'; import { ITerminalInstanceService, ITerminalInstance, ITerminalExternalLinkProvider } from 'vs/workbench/contrib/terminal/browser/terminal';
import { TerminalProcessManager } from 'vs/workbench/contrib/terminal/browser/terminalProcessManager'; import { TerminalProcessManager } from 'vs/workbench/contrib/terminal/browser/terminalProcessManager';
import type { Terminal as XTermTerminal, IBuffer, ITerminalAddon } from 'xterm'; import type { Terminal as XTermTerminal, IBuffer, ITerminalAddon } from 'xterm';
import type { SearchAddon, ISearchOptions } from 'xterm-addon-search'; import type { SearchAddon, ISearchOptions } from 'xterm-addon-search';
@@ -47,7 +47,7 @@ import { TypeAheadAddon } from 'vs/workbench/contrib/terminal/browser/terminalTy
import { BrowserFeatures } from 'vs/base/browser/canIUse'; import { BrowserFeatures } from 'vs/base/browser/canIUse';
import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences';
import { IEnvironmentVariableInfo } from 'vs/workbench/contrib/terminal/common/environmentVariable'; import { IEnvironmentVariableInfo } from 'vs/workbench/contrib/terminal/common/environmentVariable';
import { IProcessDataEvent, IShellLaunchConfig, ITerminalDimensionsOverride, ITerminalLaunchError } from 'vs/platform/terminal/common/terminal'; import { IProcessDataEvent, IShellLaunchConfig, ITerminalDimensionsOverride, ITerminalLaunchError, TerminalShellType } from 'vs/platform/terminal/common/terminal';
// How long in milliseconds should an average frame take to render for a notification to appear // How long in milliseconds should an average frame take to render for a notification to appear
// which suggests the fallback DOM-based renderer // which suggests the fallback DOM-based renderer
@@ -99,7 +99,6 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
private _cols: number = 0; private _cols: number = 0;
private _rows: number = 0; private _rows: number = 0;
private _dimensionsOverride: ITerminalDimensionsOverride | undefined; private _dimensionsOverride: ITerminalDimensionsOverride | undefined;
private _windowsShellHelper: IWindowsShellHelper | undefined;
private _xtermReadyPromise: Promise<XTermTerminal>; private _xtermReadyPromise: Promise<XTermTerminal>;
private _titleReadyPromise: Promise<string>; private _titleReadyPromise: Promise<string>;
private _titleReadyComplete: ((title: string) => any) | undefined; private _titleReadyComplete: ((title: string) => any) | undefined;
@@ -648,7 +647,6 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
this._terminalFocusContextKey.reset(); this._terminalFocusContextKey.reset();
this._refreshSelectionContextKey(); this._refreshSelectionContextKey();
})); }));
this._widgetManager.attachToElement(xterm.element); this._widgetManager.attachToElement(xterm.element);
this._processManager.onProcessReady(() => this._linkManager?.setWidgetManager(this._widgetManager)); this._processManager.onProcessReady(() => this._linkManager?.setWidgetManager(this._widgetManager));
@@ -773,8 +771,6 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
public dispose(immediate?: boolean): void { public dispose(immediate?: boolean): void {
this._logService.trace(`terminalInstance#dispose (instanceId: ${this.instanceId})`); this._logService.trace(`terminalInstance#dispose (instanceId: ${this.instanceId})`);
dispose(this._windowsShellHelper);
this._windowsShellHelper = undefined;
dispose(this._linkManager); dispose(this._linkManager);
this._linkManager = undefined; this._linkManager = undefined;
dispose(this._commandTrackerAddon); dispose(this._commandTrackerAddon);
@@ -969,25 +965,9 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
this._messageTitleDisposable = this._processManager.onProcessTitle(title => this.setTitle(title ? title : '', TitleEventSource.Process)); this._messageTitleDisposable = this._processManager.onProcessTitle(title => this.setTitle(title ? title : '', TitleEventSource.Process));
} }
} }
this._processManager.onProcessShellTypeChanged(type => {
if (platform.isWindows) { this.setShellType(type);
this._processManager.ptyProcessReady.then(() => { });
if (this._processManager.remoteAuthority) {
return;
}
this._xtermReadyPromise.then(xterm => {
if (!this._isDisposed && this._processManager && this._processManager.shellProcessId) {
this._windowsShellHelper = this._terminalInstanceService.createWindowsShellHelper(this._processManager.shellProcessId, xterm);
this._windowsShellHelper.onShellNameChange(title => {
this.setShellType(this.getShellType(title));
if (this.isTitleSetByProcess && !this._configHelper.config.experimentalUseTitleEvent) {
this.setTitle(title, TitleEventSource.Process);
}
});
}
});
});
}
} }
private _createProcess(): void { private _createProcess(): void {
@@ -998,29 +978,6 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
}); });
} }
private getShellType(executable: string): TerminalShellType {
switch (executable.toLowerCase()) {
case 'cmd.exe':
return WindowsShellType.CommandPrompt;
case 'powershell.exe':
case 'pwsh.exe':
return WindowsShellType.PowerShell;
case 'bash.exe':
case 'git-cmd.exe':
return WindowsShellType.GitBash;
case 'wsl.exe':
case 'ubuntu.exe':
case 'ubuntu1804.exe':
case 'kali.exe':
case 'debian.exe':
case 'opensuse-42.exe':
case 'sles-12.exe':
return WindowsShellType.Wsl;
default:
return undefined;
}
}
private _onProcessData(ev: IProcessDataEvent): void { private _onProcessData(ev: IProcessDataEvent): void {
if (ev.sync) { if (ev.sync) {
this._xtermCore?.writeSync(ev.data); this._xtermCore?.writeSync(ev.data);
@@ -1541,8 +1498,6 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
// automatically updates the terminal name // automatically updates the terminal name
dispose(this._messageTitleDisposable); dispose(this._messageTitleDisposable);
this._messageTitleDisposable = undefined; this._messageTitleDisposable = undefined;
dispose(this._windowsShellHelper);
this._windowsShellHelper = undefined;
break; break;
} }
const didTitleChange = title !== this._title; const didTitleChange = title !== this._title;
@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
import { ITerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminal'; import { ITerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminal';
import { IWindowsShellHelper, IDefaultShellAndArgsRequest } from 'vs/workbench/contrib/terminal/common/terminal'; import { IDefaultShellAndArgsRequest } from 'vs/workbench/contrib/terminal/common/terminal';
import type { Terminal as XTermTerminal } from 'xterm'; import type { Terminal as XTermTerminal } from 'xterm';
import type { SearchAddon as XTermSearchAddon } from 'xterm-addon-search'; import type { SearchAddon as XTermSearchAddon } from 'xterm-addon-search';
import type { Unicode11Addon as XTermUnicode11Addon } from 'xterm-addon-unicode11'; import type { Unicode11Addon as XTermUnicode11Addon } from 'xterm-addon-unicode11';
@@ -59,10 +59,6 @@ export class TerminalInstanceService extends Disposable implements ITerminalInst
return WebglAddon; return WebglAddon;
} }
public createWindowsShellHelper(): IWindowsShellHelper {
throw new Error('Not implemented');
}
public createTerminalProcess(): Promise<ITerminalChildProcess> { public createTerminalProcess(): Promise<ITerminalChildProcess> {
throw new Error('Not implemented'); throw new Error('Not implemented');
} }
@@ -5,7 +5,7 @@
import { Emitter, Event } from 'vs/base/common/event'; import { Emitter, Event } from 'vs/base/common/event';
import { Disposable } from 'vs/base/common/lifecycle'; import { Disposable } from 'vs/base/common/lifecycle';
import { IShellLaunchConfig, ITerminalChildProcess, ITerminalDimensions, ITerminalDimensionsOverride, ITerminalLaunchError } from 'vs/platform/terminal/common/terminal'; import { IShellLaunchConfig, ITerminalChildProcess, ITerminalDimensions, ITerminalDimensionsOverride, ITerminalLaunchError, TerminalShellType } from 'vs/platform/terminal/common/terminal';
import { ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; import { ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal';
import { ITerminalProcessExtHostProxy } from 'vs/workbench/contrib/terminal/common/terminal'; import { ITerminalProcessExtHostProxy } from 'vs/workbench/contrib/terminal/common/terminal';
@@ -42,6 +42,8 @@ export class TerminalProcessExtHostProxy extends Disposable implements ITerminal
public readonly onRequestCwd: Event<void> = this._onRequestCwd.event; public readonly onRequestCwd: Event<void> = this._onRequestCwd.event;
private readonly _onRequestLatency = this._register(new Emitter<void>()); private readonly _onRequestLatency = this._register(new Emitter<void>());
public readonly onRequestLatency: Event<void> = this._onRequestLatency.event; public readonly onRequestLatency: Event<void> = this._onRequestLatency.event;
private readonly _onProcessShellTypeChanged = this._register(new Emitter<TerminalShellType>());
public readonly onProcessShellTypeChanged = this._onProcessShellTypeChanged.event;
private _pendingInitialCwdRequests: ((value: string | PromiseLike<string>) => void)[] = []; private _pendingInitialCwdRequests: ((value: string | PromiseLike<string>) => void)[] = [];
private _pendingCwdRequests: ((value: string | PromiseLike<string>) => void)[] = []; private _pendingCwdRequests: ((value: string | PromiseLike<string>) => void)[] = [];
@@ -27,7 +27,7 @@ import { EnvironmentVariableInfoChangesActive, EnvironmentVariableInfoStale } fr
import { IPathService } from 'vs/workbench/services/path/common/pathService'; import { IPathService } from 'vs/workbench/services/path/common/pathService';
import { URI } from 'vs/base/common/uri'; import { URI } from 'vs/base/common/uri';
import { IEnvironmentVariableInfo, IEnvironmentVariableService, IMergedEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariable'; import { IEnvironmentVariableInfo, IEnvironmentVariableService, IMergedEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariable';
import { IProcessDataEvent, IShellLaunchConfig, ITerminalChildProcess, ITerminalDimensionsOverride, ITerminalEnvironment, ITerminalLaunchError, FlowControlConstants, ILocalTerminalService } from 'vs/platform/terminal/common/terminal'; import { IProcessDataEvent, IShellLaunchConfig, ITerminalChildProcess, ITerminalDimensionsOverride, ITerminalEnvironment, ITerminalLaunchError, FlowControlConstants, TerminalShellType, ILocalTerminalService } from 'vs/platform/terminal/common/terminal';
/** The amount of time to consider terminal errors to be related to the launch */ /** The amount of time to consider terminal errors to be related to the launch */
const LAUNCHING_DURATION = 500; const LAUNCHING_DURATION = 500;
@@ -84,6 +84,8 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce
public get onProcessData(): Event<IProcessDataEvent> { return this._onProcessData.event; } public get onProcessData(): Event<IProcessDataEvent> { return this._onProcessData.event; }
private readonly _onProcessTitle = this._register(new Emitter<string>()); private readonly _onProcessTitle = this._register(new Emitter<string>());
public get onProcessTitle(): Event<string> { return this._onProcessTitle.event; } public get onProcessTitle(): Event<string> { return this._onProcessTitle.event; }
private readonly _onProcessShellTypeChanged = this._register(new Emitter<TerminalShellType>());
public get onProcessShellTypeChanged(): Event<TerminalShellType> { return this._onProcessShellTypeChanged.event; }
private readonly _onProcessExit = this._register(new Emitter<number | undefined>()); private readonly _onProcessExit = this._register(new Emitter<number | undefined>());
public get onProcessExit(): Event<number | undefined> { return this._onProcessExit.event; } public get onProcessExit(): Event<number | undefined> { return this._onProcessExit.event; }
private readonly _onProcessOverrideDimensions = this._register(new Emitter<ITerminalDimensionsOverride | undefined>()); private readonly _onProcessOverrideDimensions = this._register(new Emitter<ITerminalDimensionsOverride | undefined>());
@@ -240,6 +242,7 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce
}); });
this._process.onProcessTitleChanged(title => this._onProcessTitle.fire(title)); this._process.onProcessTitleChanged(title => this._onProcessTitle.fire(title));
this._process.onProcessShellTypeChanged(type => this._onProcessShellTypeChanged.fire(type));
this._process.onProcessExit(exitCode => this._onExit(exitCode)); this._process.onProcessExit(exitCode => this._onExit(exitCode));
if (this._process.onProcessOverrideDimensions) { if (this._process.onProcessOverrideDimensions) {
this._process.onProcessOverrideDimensions(e => this._onProcessOverrideDimensions.fire(e)); this._process.onProcessOverrideDimensions(e => this._onProcessOverrideDimensions.fire(e));
@@ -17,9 +17,9 @@ import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { IInstantiationService, optional } from 'vs/platform/instantiation/common/instantiation'; import { IInstantiationService, optional } from 'vs/platform/instantiation/common/instantiation';
import { IPickOptions, IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; import { IPickOptions, IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { ILocalTerminalService, IShellLaunchConfig, ITerminalLaunchError, ITerminalsLayoutInfo, ITerminalsLayoutInfoById } from 'vs/platform/terminal/common/terminal'; import { ILocalTerminalService, IShellLaunchConfig, ITerminalLaunchError, ITerminalsLayoutInfo, ITerminalsLayoutInfoById, TerminalShellType, WindowsShellType } from 'vs/platform/terminal/common/terminal';
import { IViewDescriptorService, IViewsService, ViewContainerLocation } from 'vs/workbench/common/views'; import { IViewDescriptorService, IViewsService, ViewContainerLocation } from 'vs/workbench/common/views';
import { IRemoteTerminalService, ITerminalExternalLinkProvider, ITerminalInstance, ITerminalService, ITerminalTab, TerminalConnectionState, TerminalShellType, WindowsShellType } from 'vs/workbench/contrib/terminal/browser/terminal'; import { IRemoteTerminalService, ITerminalExternalLinkProvider, ITerminalInstance, ITerminalService, ITerminalTab, TerminalConnectionState } from 'vs/workbench/contrib/terminal/browser/terminal';
import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminalConfigHelper'; import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminalConfigHelper';
import { TerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminalInstance'; import { TerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminalInstance';
import { TerminalTab } from 'vs/workbench/contrib/terminal/browser/terminalTab'; import { TerminalTab } from 'vs/workbench/contrib/terminal/browser/terminalTab';
@@ -9,7 +9,7 @@ import { IDisposable } from 'vs/base/common/lifecycle';
import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { RawContextKey } from 'vs/platform/contextkey/common/contextkey';
import { OperatingSystem } from 'vs/base/common/platform'; import { OperatingSystem } from 'vs/base/common/platform';
import { IExtensionPointDescriptor } from 'vs/workbench/services/extensions/common/extensionsRegistry'; import { IExtensionPointDescriptor } from 'vs/workbench/services/extensions/common/extensionsRegistry';
import { IProcessDataEvent, IShellLaunchConfig, ITerminalDimensions, ITerminalDimensionsOverride, ITerminalLaunchError } from 'vs/platform/terminal/common/terminal'; import { IProcessDataEvent, IShellLaunchConfig, ITerminalDimensions, ITerminalDimensionsOverride, ITerminalLaunchError, TerminalShellType } from 'vs/platform/terminal/common/terminal';
import { IEnvironmentVariableInfo } from 'vs/workbench/contrib/terminal/common/environmentVariable'; import { IEnvironmentVariableInfo } from 'vs/workbench/contrib/terminal/common/environmentVariable';
export const TERMINAL_VIEW_ID = 'terminal'; export const TERMINAL_VIEW_ID = 'terminal';
@@ -231,12 +231,6 @@ export interface IDefaultShellAndArgsRequest {
callback: (shell: string, args: string[] | string | undefined) => void; callback: (shell: string, args: string[] | string | undefined) => void;
} }
export interface IWindowsShellHelper extends IDisposable {
readonly onShellNameChange: Event<string>;
getShellName(): Promise<string>;
}
export interface ITerminalProcessManager extends IDisposable { export interface ITerminalProcessManager extends IDisposable {
readonly processState: ProcessState; readonly processState: ProcessState;
readonly ptyProcessReady: Promise<void>; readonly ptyProcessReady: Promise<void>;
@@ -258,6 +252,7 @@ export interface ITerminalProcessManager extends IDisposable {
readonly onBeforeProcessData: Event<IBeforeProcessDataEvent>; readonly onBeforeProcessData: Event<IBeforeProcessDataEvent>;
readonly onProcessData: Event<IProcessDataEvent>; readonly onProcessData: Event<IProcessDataEvent>;
readonly onProcessTitle: Event<string>; readonly onProcessTitle: Event<string>;
readonly onProcessShellTypeChanged: Event<TerminalShellType>;
readonly onProcessExit: Event<number | undefined>; readonly onProcessExit: Event<number | undefined>;
readonly onProcessOverrideDimensions: Event<ITerminalDimensionsOverride | undefined>; readonly onProcessOverrideDimensions: Event<ITerminalDimensionsOverride | undefined>;
readonly onProcessResolvedShellLaunchConfig: Event<IShellLaunchConfig>; readonly onProcessResolvedShellLaunchConfig: Event<IShellLaunchConfig>;
@@ -347,12 +342,6 @@ export enum TitleEventSource {
Sequence Sequence
} }
export interface IWindowsShellHelper extends IDisposable {
readonly onShellNameChange: Event<string>;
getShellName(): Promise<string>;
}
export const enum TERMINAL_COMMAND_ID { export const enum TERMINAL_COMMAND_ID {
FIND_NEXT = 'workbench.action.terminal.findNext', FIND_NEXT = 'workbench.action.terminal.findNext',
FIND_PREVIOUS = 'workbench.action.terminal.findPrevious', FIND_PREVIOUS = 'workbench.action.terminal.findPrevious',
@@ -2,7 +2,8 @@
* Copyright (c) Microsoft Corporation. All rights reserved. * Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information. * Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
import { ITerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminal';
import { IS_WORKSPACE_SHELL_ALLOWED_STORAGE_KEY } from 'vs/workbench/contrib/terminal/common/terminal';
import { Disposable } from 'vs/base/common/lifecycle'; import { Disposable } from 'vs/base/common/lifecycle';
import { IProcessEnvironment, platform, Platform } from 'vs/base/common/platform'; import { IProcessEnvironment, platform, Platform } from 'vs/base/common/platform';
import { getSystemShell } from 'vs/base/node/shell'; import { getSystemShell } from 'vs/base/node/shell';
@@ -10,10 +11,6 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
import { ILogService } from 'vs/platform/log/common/log'; import { ILogService } from 'vs/platform/log/common/log';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { ITerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminal';
import { IS_WORKSPACE_SHELL_ALLOWED_STORAGE_KEY, IWindowsShellHelper } from 'vs/workbench/contrib/terminal/common/terminal';
import { createVariableResolver, getDefaultShell, getDefaultShellArgs } from 'vs/workbench/contrib/terminal/common/terminalEnvironment';
import { WindowsShellHelper } from 'vs/workbench/contrib/terminal/electron-browser/windowsShellHelper';
import { getMainProcessParentEnv } from 'vs/workbench/contrib/terminal/node/terminalEnvironment'; import { getMainProcessParentEnv } from 'vs/workbench/contrib/terminal/node/terminalEnvironment';
import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver'; import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver';
import { IHistoryService } from 'vs/workbench/services/history/common/history'; import { IHistoryService } from 'vs/workbench/services/history/common/history';
@@ -21,6 +18,7 @@ import type { Terminal as XTermTerminal } from 'xterm';
import type { SearchAddon as XTermSearchAddon } from 'xterm-addon-search'; import type { SearchAddon as XTermSearchAddon } from 'xterm-addon-search';
import type { Unicode11Addon as XTermUnicode11Addon } from 'xterm-addon-unicode11'; import type { Unicode11Addon as XTermUnicode11Addon } from 'xterm-addon-unicode11';
import type { WebglAddon as XTermWebglAddon } from 'xterm-addon-webgl'; import type { WebglAddon as XTermWebglAddon } from 'xterm-addon-webgl';
import { createVariableResolver, getDefaultShell, getDefaultShellArgs } from 'vs/workbench/contrib/terminal/common/terminalEnvironment';
let Terminal: typeof XTermTerminal; let Terminal: typeof XTermTerminal;
let SearchAddon: typeof XTermSearchAddon; let SearchAddon: typeof XTermSearchAddon;
@@ -69,10 +67,6 @@ export class TerminalInstanceService extends Disposable implements ITerminalInst
return WebglAddon; return WebglAddon;
} }
public createWindowsShellHelper(shellProcessId: number, xterm: XTermTerminal): IWindowsShellHelper {
return new WindowsShellHelper(shellProcessId, xterm);
}
private _isWorkspaceShellAllowed(): boolean { private _isWorkspaceShellAllowed(): boolean {
return this._storageService.getBoolean(IS_WORKSPACE_SHELL_ALLOWED_STORAGE_KEY, StorageScope.WORKSPACE, false); return this._storageService.getBoolean(IS_WORKSPACE_SHELL_ALLOWED_STORAGE_KEY, StorageScope.WORKSPACE, false);
} }
@@ -6,7 +6,7 @@
import { Emitter } from 'vs/base/common/event'; import { Emitter } from 'vs/base/common/event';
import { Disposable } from 'vs/base/common/lifecycle'; import { Disposable } from 'vs/base/common/lifecycle';
import { ILocalPtyService } from 'vs/platform/terminal/electron-sandbox/terminal'; import { ILocalPtyService } from 'vs/platform/terminal/electron-sandbox/terminal';
import { IProcessDataEvent, IShellLaunchConfig, ITerminalChildProcess, ITerminalDimensionsOverride, ITerminalLaunchError } from 'vs/platform/terminal/common/terminal'; import { IProcessDataEvent, IShellLaunchConfig, ITerminalChildProcess, ITerminalDimensionsOverride, ITerminalLaunchError, TerminalShellType } from 'vs/platform/terminal/common/terminal';
import { IPtyHostProcessReplayEvent } from 'vs/platform/terminal/common/terminalProcess'; import { IPtyHostProcessReplayEvent } from 'vs/platform/terminal/common/terminalProcess';
/** /**
@@ -30,6 +30,8 @@ export class LocalPty extends Disposable implements ITerminalChildProcess {
public readonly onProcessOverrideDimensions = this._onProcessOverrideDimensions.event; public readonly onProcessOverrideDimensions = this._onProcessOverrideDimensions.event;
private readonly _onProcessResolvedShellLaunchConfig = this._register(new Emitter<IShellLaunchConfig>()); private readonly _onProcessResolvedShellLaunchConfig = this._register(new Emitter<IShellLaunchConfig>());
public readonly onProcessResolvedShellLaunchConfig = this._onProcessResolvedShellLaunchConfig.event; public readonly onProcessResolvedShellLaunchConfig = this._onProcessResolvedShellLaunchConfig.event;
private readonly _onProcessShellTypeChanged = this._register(new Emitter<TerminalShellType>());
public readonly onProcessShellTypeChanged = this._onProcessShellTypeChanged.event;
constructor( constructor(
readonly id: number, readonly id: number,
@@ -95,6 +97,9 @@ export class LocalPty extends Disposable implements ITerminalChildProcess {
handleTitleChanged(e: string) { handleTitleChanged(e: string) {
this._onProcessTitleChanged.fire(e); this._onProcessTitleChanged.fire(e);
} }
handleShellTypeChanged(e: TerminalShellType) {
this._onProcessShellTypeChanged.fire(e);
}
handleOverrideDimensions(e: ITerminalDimensionsOverride | undefined) { handleOverrideDimensions(e: ITerminalDimensionsOverride | undefined) {
this._onProcessOverrideDimensions.fire(e); this._onProcessOverrideDimensions.fire(e);
} }
@@ -1477,7 +1477,6 @@ export class TestTerminalInstanceService implements ITerminalInstanceService {
createWindowsShellHelper(shellProcessId: number, xterm: any): any { throw new Error('Method not implemented.'); } createWindowsShellHelper(shellProcessId: number, xterm: any): any { throw new Error('Method not implemented.'); }
} }
export class TestLocalTerminalService implements ILocalTerminalService { export class TestLocalTerminalService implements ILocalTerminalService {
declare readonly _serviceBrand: undefined; declare readonly _serviceBrand: undefined;
@@ -1507,7 +1506,7 @@ class TestTerminalChildProcess implements ITerminalChildProcess {
onProcessExit = Event.None; onProcessExit = Event.None;
onProcessReady = Event.None; onProcessReady = Event.None;
onProcessTitleChanged = Event.None; onProcessTitleChanged = Event.None;
onProcessShellTypeChanged = Event.None;
async start(): Promise<undefined> { return undefined; } async start(): Promise<undefined> { return undefined; }
shutdown(immediate: boolean): void { } shutdown(immediate: boolean): void { }
input(data: string): void { } input(data: string): void { }