Revert "Merge from vscode ada4bddb8edc69eea6ebaaa0e88c5f903cbd43d8 (#5529)" (#5553)

This reverts commit 5d44b6a6a7.
This commit is contained in:
Anthony Dresser
2019-05-20 17:07:32 -07:00
committed by GitHub
parent 1315b8e42a
commit c9a4f8f664
325 changed files with 3332 additions and 4501 deletions

View File

@@ -502,7 +502,7 @@ actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FindPrevious, Fi
const sendSequenceTerminalCommand = new SendSequenceTerminalCommand({
id: SendSequenceTerminalCommand.ID,
precondition: undefined,
precondition: null,
description: {
description: `Send Custom Sequence To Terminal`,
args: [{

View File

@@ -6,7 +6,7 @@
import { Terminal as XTermTerminal } from 'vscode-xterm';
import { ITerminalInstance, IWindowsShellHelper, ITerminalProcessManager, ITerminalConfigHelper, ITerminalChildProcess, IShellLaunchConfig } from 'vs/workbench/contrib/terminal/common/terminal';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IProcessEnvironment, Platform } from 'vs/base/common/platform';
import { IProcessEnvironment } from 'vs/base/common/platform';
export const ITerminalInstanceService = createDecorator<ITerminalInstanceService>('terminalInstanceService');
@@ -17,7 +17,6 @@ export interface ITerminalInstanceService {
createWindowsShellHelper(shellProcessId: number, instance: ITerminalInstance, xterm: XTermTerminal): IWindowsShellHelper;
createTerminalProcessManager(id: number, configHelper: ITerminalConfigHelper): ITerminalProcessManager;
createTerminalProcess(shellLaunchConfig: IShellLaunchConfig, cwd: string, cols: number, rows: number, env: IProcessEnvironment, windowsEnableConpty: boolean): ITerminalChildProcess;
getDefaultShell(p: Platform): string;
}
export interface IBrowserTerminalConfigHelper extends ITerminalConfigHelper {

View File

@@ -8,7 +8,7 @@ import { Action, IAction } from 'vs/base/common/actions';
import { EndOfLinePreference } from 'vs/editor/common/model';
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
import { ITerminalService, TERMINAL_PANEL_ID, ITerminalInstance, Direction, ITerminalConfigHelper } from 'vs/workbench/contrib/terminal/common/terminal';
import { SelectActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar';
import { SelectActionItem } from 'vs/base/browser/ui/actionbar/actionbar';
import { TogglePanelAction } from 'vs/workbench/browser/panel';
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
@@ -726,7 +726,7 @@ export class SwitchTerminalAction extends Action {
}
}
export class SwitchTerminalActionViewItem extends SelectActionViewItem {
export class SwitchTerminalActionItem extends SelectActionItem {
constructor(
action: IAction,

View File

@@ -30,7 +30,7 @@ export class TerminalCommandTracker implements ITerminalCommandTracker, IDisposa
constructor(
private _xterm: Terminal
) {
this._xterm.onKey(e => this._onKey(e.key));
this._xterm.on('key', key => this._onKey(key));
}
public dispose(): void {
@@ -48,7 +48,7 @@ export class TerminalCommandTracker implements ITerminalCommandTracker, IDisposa
}
private _onEnter(): void {
if (this._xterm.buffer.cursorX >= MINIMUM_PROMPT_LENGTH) {
if (this._xterm._core.buffer.x >= MINIMUM_PROMPT_LENGTH) {
this._xterm.addMarker(0);
}
}
@@ -175,7 +175,7 @@ export class TerminalCommandTracker implements ITerminalCommandTracker, IDisposa
private _getLine(marker: IMarker | Boundary): number {
// Use the _second last_ row as the last row is likely the prompt
if (marker === Boundary.Bottom) {
return this._xterm.buffer.baseY + this._xterm.rows - 1;
return this._xterm._core.buffer.ybase + this._xterm.rows - 1;
}
if (marker === Boundary.Top) {
@@ -235,10 +235,10 @@ export class TerminalCommandTracker implements ITerminalCommandTracker, IDisposa
if (this._currentMarker === Boundary.Bottom) {
return 0;
} else if (this._currentMarker === Boundary.Top) {
return 0 - (this._xterm.buffer.baseY + this._xterm.buffer.cursorY);
return 0 - (this._xterm._core.buffer.ybase + this._xterm._core.buffer.y);
} else {
let offset = this._getLine(this._currentMarker);
offset -= this._xterm.buffer.baseY + this._xterm.buffer.cursorY;
offset -= this._xterm._core.buffer.ybase + this._xterm._core.buffer.y;
return offset;
}
}

View File

@@ -227,9 +227,9 @@ export class TerminalConfigHelper implements IBrowserTerminalConfigHelper {
return !!isWorkspaceShellAllowed;
}
public mergeDefaultShellPathAndArgs(shell: IShellLaunchConfig, defaultShell: string, platformOverride: platform.Platform = platform.platform): void {
public mergeDefaultShellPathAndArgs(shell: IShellLaunchConfig, platformOverride: platform.Platform = platform.platform): void {
const isWorkspaceShellAllowed = this.checkWorkspaceShellPermissions(platformOverride === platform.Platform.Windows ? platform.OperatingSystem.Windows : (platformOverride === platform.Platform.Mac ? platform.OperatingSystem.Macintosh : platform.OperatingSystem.Linux));
mergeDefaultShellPathAndArgs(shell, (key) => this._workspaceConfigurationService.inspect(key), isWorkspaceShellAllowed, defaultShell, platformOverride);
mergeDefaultShellPathAndArgs(shell, (key) => this._workspaceConfigurationService.inspect(key), isWorkspaceShellAllowed, platformOverride);
}
private _toInteger(source: any, minimum: number, maximum: number, fallback: number): number {

View File

@@ -25,14 +25,14 @@ import { activeContrastBorder, scrollbarSliderActiveBackground, scrollbarSliderB
import { ICssStyleCollector, ITheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
import { PANEL_BACKGROUND } from 'vs/workbench/common/theme';
import { TerminalWidgetManager } from 'vs/workbench/contrib/terminal/browser/terminalWidgetManager';
import { IShellLaunchConfig, ITerminalDimensions, ITerminalInstance, ITerminalProcessManager, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, NEVER_MEASURE_RENDER_TIME_STORAGE_KEY, ProcessState, TERMINAL_PANEL_ID, IWindowsShellHelper, SHELL_PATH_INVALID_EXIT_CODE } from 'vs/workbench/contrib/terminal/common/terminal';
import { IShellLaunchConfig, ITerminalDimensions, ITerminalInstance, ITerminalProcessManager, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, NEVER_MEASURE_RENDER_TIME_STORAGE_KEY, ProcessState, TERMINAL_PANEL_ID, IWindowsShellHelper } 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 { TERMINAL_COMMAND_ID } from 'vs/workbench/contrib/terminal/common/terminalCommands';
import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminalConfigHelper';
import { TerminalLinkHandler } from 'vs/workbench/contrib/terminal/browser/terminalLinkHandler';
import { TerminalCommandTracker } from 'vs/workbench/contrib/terminal/browser/terminalCommandTracker';
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
import { ISearchOptions, Terminal as XTermTerminal, IBuffer } from 'vscode-xterm';
import { ISearchOptions, Terminal as XTermTerminal } from 'vscode-xterm';
import { IAccessibilityService, AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility';
import { ITerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminal';
@@ -371,7 +371,7 @@ export class TerminalInstance implements ITerminalInstance {
// it gets removed and then added back to the DOM (resetting scrollTop to 0).
// Upstream issue: https://github.com/sourcelair/xterm.js/issues/291
if (this._xterm) {
this._xterm._core._onScroll.fire(this._xterm.buffer.viewportY);
this._xterm.emit('scroll', this._xterm._core.buffer.ydisp);
}
}
@@ -416,17 +416,18 @@ export class TerminalInstance implements ITerminalInstance {
// TODO: Guess whether to use canvas or dom better
rendererType: config.rendererType === 'auto' ? 'canvas' : config.rendererType,
// TODO: Remove this once the setting is removed upstream
experimentalCharAtlas: 'dynamic'
experimentalCharAtlas: 'dynamic',
experimentalBufferLineImpl: 'TypedArray'
});
if (this._shellLaunchConfig.initialText) {
this._xterm.writeln(this._shellLaunchConfig.initialText);
}
this._xterm.onLineFeed(() => this._onLineFeed());
this._xterm.onKey(e => this._onKey(e.key, e.domEvent));
this._xterm.on('linefeed', () => this._onLineFeed());
this._xterm.on('key', (key, ev) => this._onKey(key, ev));
if (this._processManager) {
this._processManager.onProcessData(data => this._onProcessData(data));
this._xterm.onData(data => this._processManager!.write(data));
this._xterm.on('data', data => this._processManager!.write(data));
// TODO: How does the cwd work on detached processes?
this.processReady.then(async () => {
this._linkHandler.processCwd = await this._processManager!.getInitialCwd();
@@ -438,24 +439,18 @@ export class TerminalInstance implements ITerminalInstance {
return;
}
if (this._processManager.os === platform.OperatingSystem.Windows) {
this._xterm.setOption('windowsMode', true);
// Force line data to be sent when the cursor is moved, the main purpose for
// this is because ConPTY will often not do a line feed but instead move the
// cursor, in which case we still want to send the current line's data to tasks.
this._xterm.addCsiHandler('H', () => {
this._onCursorMove();
return false;
});
this._xterm.winptyCompatInit();
}
this._linkHandler = this._instantiationService.createInstance(TerminalLinkHandler, this._xterm, platform.platform, this._processManager);
});
} else if (this.shellLaunchConfig.isRendererOnly) {
this._linkHandler = this._instantiationService.createInstance(TerminalLinkHandler, this._xterm, undefined, undefined);
}
this._xterm.on('focus', () => this._onFocus.fire(this));
// Register listener to trigger the onInput ext API if the terminal is a renderer only
if (this._shellLaunchConfig.isRendererOnly) {
this._xterm.onData(data => this._sendRendererInput(data));
this._xterm.on('data', (data) => this._sendRendererInput(data));
}
this._commandTracker = new TerminalCommandTracker(this._xterm);
@@ -513,7 +508,6 @@ export class TerminalInstance implements ITerminalInstance {
(<any>this._wrapperElement).xterm = this._xterm;
this._xterm.open(this._xtermElement);
this._xterm.textarea.addEventListener('focus', () => this._onFocus.fire(this));
this._xterm.attachCustomKeyEventHandler((event: KeyboardEvent): boolean => {
// Disable all input if the terminal is exiting
if (this._isExiting) {
@@ -751,8 +745,8 @@ export class TerminalInstance implements ITerminalInstance {
}
}
if (this._xterm) {
const buffer = this._xterm.buffer;
this._sendLineData(buffer, buffer.baseY + buffer.cursorY);
const buffer = (<any>this._xterm._core.buffer);
this._sendLineData(buffer, buffer.ybase + buffer.y);
this._xterm.dispose();
}
@@ -865,7 +859,7 @@ export class TerminalInstance implements ITerminalInstance {
// necessary if the number of rows in the terminal has decreased while it was in the
// background since scrollTop changes take no effect but the terminal's position does
// change since the number of visible rows decreases.
this._xterm._core._onScroll.fire(this._xterm.buffer.viewportY);
this._xterm.emit('scroll', this._xterm._core.buffer.ydisp);
if (this._container && this._container.parentElement) {
// Force a layout when the instance becomes invisible. This is particularly important
// for ensuring that terminals that are created in the background by an extension will
@@ -976,32 +970,10 @@ export class TerminalInstance implements ITerminalInstance {
}
this._isExiting = true;
let exitCodeMessage: string | undefined;
let exitCodeMessage: string;
// Create exit code message
if (exitCode) {
if (exitCode === SHELL_PATH_INVALID_EXIT_CODE) {
exitCodeMessage = nls.localize('terminal.integrated.exitedWithInvalidPath', 'The terminal shell path does not exist: {0}', this._shellLaunchConfig.executable);
} else if (this._processManager && this._processManager.processState === ProcessState.KILLED_DURING_LAUNCH) {
let args = '';
if (typeof this._shellLaunchConfig.args === 'string') {
args = ` ${this._shellLaunchConfig.args}`;
} else if (this._shellLaunchConfig.args && this._shellLaunchConfig.args.length) {
args = ' ' + this._shellLaunchConfig.args.map(a => {
if (typeof a === 'string' && a.indexOf(' ') !== -1) {
return `'${a}'`;
}
return a;
}).join(' ');
}
if (this._shellLaunchConfig.executable) {
exitCodeMessage = nls.localize('terminal.integrated.launchFailed', 'The terminal process command \'{0}{1}\' failed to launch (exit code: {2})', this._shellLaunchConfig.executable, args, exitCode);
} else {
exitCodeMessage = nls.localize('terminal.integrated.launchFailedExtHost', 'The terminal process failed to launch (exit code: {0})', exitCode);
}
} else {
exitCodeMessage = nls.localize('terminal.integrated.exitedWithCode', 'The terminal process terminated with exit code: {0}', exitCode);
}
exitCodeMessage = nls.localize('terminal.integrated.exitedWithCode', 'The terminal process terminated with exit code: {0}', exitCode);
}
this._logService.debug(`Terminal process exit (id: ${this.id})${this._processManager ? ' state ' + this._processManager.processState : ''}`);
@@ -1009,8 +981,8 @@ export class TerminalInstance implements ITerminalInstance {
// Only trigger wait on exit when the exit was *not* triggered by the
// user (via the `workbench.action.terminal.kill` command).
if (this._shellLaunchConfig.waitOnExit && (!this._processManager || this._processManager.processState !== ProcessState.KILLED_BY_USER)) {
if (exitCodeMessage) {
this._xterm.writeln(exitCodeMessage);
if (exitCode) {
this._xterm.writeln(exitCodeMessage!);
}
if (typeof this._shellLaunchConfig.waitOnExit === 'string') {
let message = this._shellLaunchConfig.waitOnExit;
@@ -1025,14 +997,29 @@ export class TerminalInstance implements ITerminalInstance {
}
} else {
this.dispose();
if (exitCodeMessage) {
if (exitCode) {
if (this._processManager && this._processManager.processState === ProcessState.KILLED_DURING_LAUNCH) {
this._notificationService.error(exitCodeMessage);
let args = '';
if (typeof this._shellLaunchConfig.args === 'string') {
args = this._shellLaunchConfig.args;
} else if (this._shellLaunchConfig.args && this._shellLaunchConfig.args.length) {
args = ' ' + this._shellLaunchConfig.args.map(a => {
if (typeof a === 'string' && a.indexOf(' ') !== -1) {
return `'${a}'`;
}
return a;
}).join(' ');
}
if (this._shellLaunchConfig.executable) {
this._notificationService.error(nls.localize('terminal.integrated.launchFailed', 'The terminal process command \'{0}{1}\' failed to launch (exit code: {2})', this._shellLaunchConfig.executable, args, exitCode));
} else {
this._notificationService.error(nls.localize('terminal.integrated.launchFailedExtHost', 'The terminal process failed to launch (exit code: {0})', exitCode));
}
} else {
if (this._configHelper.config.showExitAlert) {
this._notificationService.error(exitCodeMessage);
this._notificationService.error(exitCodeMessage!);
} else {
console.warn(exitCodeMessage);
console.warn(exitCodeMessage!);
}
}
}
@@ -1114,22 +1101,17 @@ export class TerminalInstance implements ITerminalInstance {
}
private _onLineFeed(): void {
const buffer = this._xterm.buffer;
const newLine = buffer.getLine(buffer.baseY + buffer.cursorY);
if (newLine && !newLine.isWrapped) {
this._sendLineData(buffer, buffer.baseY + buffer.cursorY - 1);
const buffer = (<any>this._xterm._core.buffer);
const newLine = buffer.lines.get(buffer.ybase + buffer.y);
if (!newLine.isWrapped) {
this._sendLineData(buffer, buffer.ybase + buffer.y - 1);
}
}
private _onCursorMove(): void {
const buffer = this._xterm.buffer;
this._sendLineData(buffer, buffer.baseY + buffer.cursorY);
}
private _sendLineData(buffer: IBuffer, lineIndex: number): void {
let lineData = buffer.getLine(lineIndex)!.translateToString(true);
while (lineIndex >= 0 && buffer.getLine(lineIndex--)!.isWrapped) {
lineData = buffer.getLine(lineIndex)!.translateToString(false) + lineData;
private _sendLineData(buffer: any, lineIndex: number): void {
let lineData = buffer.translateBufferLineToString(lineIndex, true);
while (lineIndex >= 0 && buffer.lines.get(lineIndex--).isWrapped) {
lineData = buffer.translateBufferLineToString(lineIndex, false) + lineData;
}
this._onLineData.fire(lineData);
}

View File

@@ -7,7 +7,7 @@ import * as dom from 'vs/base/browser/dom';
import * as nls from 'vs/nls';
import * as platform from 'vs/base/common/platform';
import { Action, IAction } from 'vs/base/common/actions';
import { IActionViewItem, Separator } from 'vs/base/browser/ui/actionbar/actionbar';
import { IActionItem, Separator } from 'vs/base/browser/ui/actionbar/actionbar';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
@@ -16,7 +16,7 @@ import { ITerminalService, TERMINAL_PANEL_ID } from 'vs/workbench/contrib/termin
import { IThemeService, ITheme, registerThemingParticipant, ICssStyleCollector } from 'vs/platform/theme/common/themeService';
import { TerminalFindWidget } from 'vs/workbench/contrib/terminal/browser/terminalFindWidget';
import { editorHoverBackground, editorHoverBorder, editorForeground } from 'vs/platform/theme/common/colorRegistry';
import { KillTerminalAction, SwitchTerminalAction, SwitchTerminalActionViewItem, CopyTerminalSelectionAction, TerminalPasteAction, ClearTerminalAction, SelectAllTerminalAction, CreateNewTerminalAction, SplitTerminalAction } from 'vs/workbench/contrib/terminal/browser/terminalActions';
import { KillTerminalAction, SwitchTerminalAction, SwitchTerminalActionItem, CopyTerminalSelectionAction, TerminalPasteAction, ClearTerminalAction, SelectAllTerminalAction, CreateNewTerminalAction, SplitTerminalAction } from 'vs/workbench/contrib/terminal/browser/terminalActions';
import { Panel } from 'vs/workbench/browser/panel';
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
import { URI } from 'vs/base/common/uri';
@@ -161,12 +161,12 @@ export class TerminalPanel extends Panel {
return this._contextMenuActions;
}
public getActionViewItem(action: Action): IActionViewItem | undefined {
public getActionItem(action: Action): IActionItem | undefined {
if (action.id === SwitchTerminalAction.ID) {
return this._instantiationService.createInstance(SwitchTerminalActionViewItem, action);
return this._instantiationService.createInstance(SwitchTerminalActionItem, action);
}
return super.getActionViewItem(action);
return super.getActionItem(action);
}
public focus(): void {

View File

@@ -169,7 +169,7 @@ export class TerminalProcessManager implements ITerminalProcessManager {
private _launchProcess(shellLaunchConfig: IShellLaunchConfig, cols: number, rows: number): ITerminalChildProcess {
if (!shellLaunchConfig.executable) {
this._configHelper.mergeDefaultShellPathAndArgs(shellLaunchConfig, this._terminalInstanceService.getDefaultShell(platform.platform));
this._configHelper.mergeDefaultShellPathAndArgs(shellLaunchConfig);
}
const activeWorkspaceRootUri = this._historyService.getLastActiveWorkspaceRoot(Schemas.file);
@@ -181,7 +181,8 @@ export class TerminalProcessManager implements ITerminalProcessManager {
const isWorkspaceShellAllowed = this._configHelper.checkWorkspaceShellPermissions();
const env = terminalEnvironment.createTerminalEnvironment(shellLaunchConfig, lastActiveWorkspace, envFromConfigValue, this._configurationResolverService, isWorkspaceShellAllowed, this._productService.version, this._configHelper.config.setLocaleVariables);
const useConpty = this._configHelper.config.windowsEnableConpty;
this._logService.debug(`Terminal process launching`, shellLaunchConfig, initialCwd, cols, rows, env);
const useConpty = (shellLaunchConfig.forceWinpty !== true) && this._configHelper.config.windowsEnableConpty;
return this._terminalInstanceService.createTerminalProcess(shellLaunchConfig, initialCwd, cols, rows, env, useConpty);
}

View File

@@ -44,7 +44,7 @@ export abstract class TerminalService extends CommonTerminalService implements I
super(contextKeyService, panelService, lifecycleService, storageService, notificationService, dialogService, extensionService, fileService, remoteAgentService);
}
public abstract getDefaultShell(p: platform.Platform): string;
protected abstract _getDefaultShell(p: platform.Platform): string;
public createInstance(terminalFocusContextKey: IContextKey<boolean>, configHelper: ITerminalConfigHelper, container: HTMLElement | undefined, shellLaunchConfig: IShellLaunchConfig, doCreateProcess: boolean): ITerminalInstance {
const instance = this._instantiationService.createInstance(TerminalInstance, terminalFocusContextKey, configHelper, container, shellLaunchConfig);
@@ -101,7 +101,7 @@ export abstract class TerminalService extends CommonTerminalService implements I
}
// Never suggest if the setting is non-default already (ie. they set the setting manually)
if (this.configHelper.config.shell.windows !== this.getDefaultShell(platform.Platform.Windows)) {
if (this.configHelper.config.shell.windows !== this._getDefaultShell(platform.Platform.Windows)) {
this._storageService.store(NEVER_SUGGEST_SELECT_WINDOWS_SHELL_STORAGE_KEY, true, StorageScope.GLOBAL);
return;
}

View File

@@ -58,7 +58,6 @@ export const TERMINAL_CONFIG_SECTION = 'terminal.integrated';
export const DEFAULT_LETTER_SPACING = 0;
export const MINIMUM_LETTER_SPACING = -5;
export const DEFAULT_LINE_HEIGHT = 1;
export const SHELL_PATH_INVALID_EXIT_CODE = -1;
export type FontWeight = 'normal' | 'bold' | '100' | '200' | '300' | '400' | '500' | '600' | '700' | '800' | '900';
@@ -113,7 +112,7 @@ export interface ITerminalConfigHelper {
/**
* Merges the default shell path and args into the provided launch configuration
*/
mergeDefaultShellPathAndArgs(shell: IShellLaunchConfig, defaultShell: string, platformOverride?: platform.Platform): void;
mergeDefaultShellPathAndArgs(shell: IShellLaunchConfig, platformOverride?: platform.Platform): void;
/** Sets whether a workspace shell configuration is allowed or not */
setWorkspaceShellAllowed(isAllowed: boolean): void;
checkWorkspaceShellPermissions(osOverride?: platform.OperatingSystem): boolean;
@@ -193,6 +192,12 @@ export interface IShellLaunchConfig {
* provided as nothing will be inherited from the process or any configuration.
*/
strictEnv?: boolean;
/**
* Moving forward, conpty will be the default. However, there are cases where conpty is not ready
* to be the default. This property will force winpty to be used, even when conpty would normally be used.
*/
forceWinpty?: boolean;
}
export interface ITerminalService {
@@ -254,7 +259,6 @@ export interface ITerminalService {
findPrevious(): void;
setContainers(panelContainer: HTMLElement, terminalContainer: HTMLElement): void;
getDefaultShell(p: platform.Platform): string;
selectDefaultWindowsShell(): Promise<string | undefined>;
setWorkspaceShellAllowed(isAllowed: boolean): void;
@@ -690,6 +694,7 @@ export const enum ProcessState {
KILLED_BY_PROCESS
}
export interface ITerminalProcessExtHostProxy extends IDisposable {
readonly terminalId: number;

View File

@@ -164,14 +164,15 @@ export function mergeDefaultShellPathAndArgs(
shell: IShellLaunchConfig,
fetchSetting: (key: string) => { user: string | string[] | undefined, value: string | string[] | undefined, default: string | string[] | undefined },
isWorkspaceShellAllowed: boolean,
defaultShell: string,
platformOverride: platform.Platform = platform.platform
): void {
const platformKey = platformOverride === platform.Platform.Windows ? 'windows' : platformOverride === platform.Platform.Mac ? 'osx' : 'linux';
const shellConfigValue = fetchSetting(`terminal.integrated.shell.${platformKey}`);
// const shellConfigValue = this._workspaceConfigurationService.inspect<string>(`terminal.integrated.shell.${platformKey}`);
const shellArgsConfigValue = fetchSetting(`terminal.integrated.shellArgs.${platformKey}`);
// const shellArgsConfigValue = this._workspaceConfigurationService.inspect<string[]>(`terminal.integrated.shellArgs.${platformKey}`);
shell.executable = (isWorkspaceShellAllowed ? <string>shellConfigValue.value : <string>shellConfigValue.user) || (<string | null>shellConfigValue.default || defaultShell);
shell.executable = (isWorkspaceShellAllowed ? <string>shellConfigValue.value : <string>shellConfigValue.user) || <string>shellConfigValue.default;
shell.args = (isWorkspaceShellAllowed ? <string[]>shellArgsConfigValue.value : <string[]>shellArgsConfigValue.user) || <string[]>shellArgsConfigValue.default;
// Change Sysnative to System32 if the OS is Windows but NOT WoW64. It's

View File

@@ -17,7 +17,7 @@ import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { IFileService } from 'vs/platform/files/common/files';
import { escapeNonWindowsPath } from 'vs/workbench/contrib/terminal/common/terminalEnvironment';
import { isWindows, Platform } from 'vs/base/common/platform';
import { isWindows } from 'vs/base/common/platform';
import { basename } from 'vs/base/common/path';
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
import { timeout } from 'vs/base/common/async';
@@ -106,7 +106,6 @@ export abstract class TerminalService implements ITerminalService {
public abstract createTerminal(shell?: IShellLaunchConfig, wasNewTerminalAction?: boolean): ITerminalInstance;
public abstract createInstance(terminalFocusContextKey: IContextKey<boolean>, configHelper: ITerminalConfigHelper, container: HTMLElement, shellLaunchConfig: IShellLaunchConfig, doCreateProcess: boolean): ITerminalInstance;
public abstract getDefaultShell(platform: Platform): string;
public abstract selectDefaultWindowsShell(): Promise<string | undefined>;
public abstract setContainers(panelContainer: HTMLElement, terminalContainer: HTMLElement): void;
@@ -427,9 +426,6 @@ export abstract class TerminalService implements ITerminalService {
return Promise.resolve(null);
}
const current = potentialPaths.shift();
if (current! === '') {
return this._validateShellPaths(label, potentialPaths);
}
return this._fileService.exists(URI.file(current!)).then(exists => {
if (!exists) {
return this._validateShellPaths(label, potentialPaths);

View File

@@ -22,19 +22,19 @@ configurationRegistry.registerConfiguration({
type: 'object',
properties: {
'terminal.integrated.shell.linux': {
markdownDescription: nls.localize('terminal.integrated.shell.linux', "The path of the shell that the terminal uses on Linux (default: {0}). [Read more about configuring the shell](https://code.visualstudio.com/docs/editor/integrated-terminal#_configuration).", getDefaultShell(platform.Platform.Linux)),
type: ['string', 'null'],
default: null
markdownDescription: nls.localize('terminal.integrated.shell.linux', "The path of the shell that the terminal uses on Linux. [Read more about configuring the shell](https://code.visualstudio.com/docs/editor/integrated-terminal#_configuration)."),
type: 'string',
default: getDefaultShell(platform.Platform.Linux)
},
'terminal.integrated.shell.osx': {
markdownDescription: nls.localize('terminal.integrated.shell.osx', "The path of the shell that the terminal uses on macOS (default: {0}). [Read more about configuring the shell](https://code.visualstudio.com/docs/editor/integrated-terminal#_configuration).", getDefaultShell(platform.Platform.Mac)),
type: ['string', 'null'],
default: null
markdownDescription: nls.localize('terminal.integrated.shell.osx', "The path of the shell that the terminal uses on macOS. [Read more about configuring the shell](https://code.visualstudio.com/docs/editor/integrated-terminal#_configuration)."),
type: 'string',
default: getDefaultShell(platform.Platform.Mac)
},
'terminal.integrated.shell.windows': {
markdownDescription: nls.localize('terminal.integrated.shell.windows', "The path of the shell that the terminal uses on Windows (default: {0}). [Read more about configuring the shell](https://code.visualstudio.com/docs/editor/integrated-terminal#_configuration).", getDefaultShell(platform.Platform.Windows)),
type: ['string', 'null'],
default: null
markdownDescription: nls.localize('terminal.integrated.shell.windows', "The path of the shell that the terminal uses on Windows. [Read more about configuring the shell](https://code.visualstudio.com/docs/editor/integrated-terminal#_configuration)."),
type: 'string',
default: getDefaultShell(platform.Platform.Windows)
}
}
});

View File

@@ -10,10 +10,9 @@ import { ITerminalInstance, IWindowsShellHelper, ITerminalConfigHelper, ITermina
import { WindowsShellHelper } from 'vs/workbench/contrib/terminal/node/windowsShellHelper';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { TerminalProcessManager } from 'vs/workbench/contrib/terminal/browser/terminalProcessManager';
import { IProcessEnvironment, Platform } from 'vs/base/common/platform';
import { IProcessEnvironment } from 'vs/base/common/platform';
import { TerminalProcess } from 'vs/workbench/contrib/terminal/node/terminalProcess';
import * as typeAheadAddon from 'vs/workbench/contrib/terminal/browser/terminalTypeAheadAddon';
import { getDefaultShell } from 'vs/workbench/contrib/terminal/node/terminal';
let Terminal: typeof XTermTerminal;
@@ -36,6 +35,7 @@ export class TerminalInstanceService implements ITerminalInstanceService {
// Enable xterm.js addons
Terminal.applyAddon(require.__$__nodeRequire('vscode-xterm/lib/addons/search/search'));
Terminal.applyAddon(require.__$__nodeRequire('vscode-xterm/lib/addons/webLinks/webLinks'));
Terminal.applyAddon(require.__$__nodeRequire('vscode-xterm/lib/addons/winptyCompat/winptyCompat'));
Terminal.applyAddon(typeAheadAddon);
// Localize strings
Terminal.strings.blankLine = nls.localize('terminal.integrated.a11yBlankLine', 'Blank line');
@@ -54,10 +54,6 @@ export class TerminalInstanceService implements ITerminalInstanceService {
}
public createTerminalProcess(shellLaunchConfig: IShellLaunchConfig, cwd: string, cols: number, rows: number, env: IProcessEnvironment, windowsEnableConpty: boolean): ITerminalChildProcess {
return this._instantiationService.createInstance(TerminalProcess, shellLaunchConfig, cwd, cols, rows, env, windowsEnableConpty);
return new TerminalProcess(shellLaunchConfig, cwd, cols, rows, env, windowsEnableConpty);
}
public getDefaultShell(p: Platform): string {
return getDefaultShell(p);
}
}
}

View File

@@ -98,7 +98,7 @@ export class TerminalService extends BrowserTerminalService implements ITerminal
});
}
public getDefaultShell(p: platform.Platform): string {
protected _getDefaultShell(p: platform.Platform): string {
return getDefaultShell(p);
}
@@ -117,28 +117,7 @@ export class TerminalService extends BrowserTerminalService implements ITerminal
});
}
/**
* Get the executable file path of shell from registry.
* @param shellName The shell name to get the executable file path
* @returns `[]` or `[ 'path' ]`
*/
private async _getShellPathFromRegistry(shellName: string): Promise<string[]> {
const Registry = await import('vscode-windows-registry');
try {
const shellPath = Registry.GetStringRegKey('HKEY_LOCAL_MACHINE', `SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\${shellName}.exe`, '');
if (shellPath === undefined) {
return [];
}
return [shellPath];
} catch (error) {
return [];
}
}
private async _detectWindowsShells(): Promise<IQuickPickItem[]> {
private _detectWindowsShells(): Promise<IQuickPickItem[]> {
// Determine the correct System32 path. We want to point to Sysnative
// when the 32-bit version of VS Code is running on a 64-bit machine.
// The reason for this is because PowerShell's important PSReadline
@@ -155,7 +134,6 @@ export class TerminalService extends BrowserTerminalService implements ITerminal
const expectedLocations = {
'Command Prompt': [`${system32Path}\\cmd.exe`],
PowerShell: [`${system32Path}\\WindowsPowerShell\\v1.0\\powershell.exe`],
'PowerShell Core': await this._getShellPathFromRegistry('pwsh'),
'WSL Bash': [`${system32Path}\\${useWSLexe ? 'wsl.exe' : 'bash.exe'}`],
'Git Bash': [
`${process.env['ProgramW6432']}\\Git\\bin\\bash.exe`,

View File

@@ -13,12 +13,11 @@ import { getWindowsBuildNumber } from 'vs/workbench/contrib/terminal/node/termin
import { IDisposable } from 'vs/base/common/lifecycle';
import { IShellLaunchConfig, ITerminalChildProcess } from 'vs/workbench/contrib/terminal/common/terminal';
import { exec } from 'child_process';
import { ILogService } from 'vs/platform/log/common/log';
export class TerminalProcess implements ITerminalChildProcess, IDisposable {
private _exitCode: number;
private _closeTimeout: any;
private _ptyProcess: pty.IPty | undefined;
private _ptyProcess: pty.IPty;
private _currentTitle: string = '';
private _processStartupComplete: Promise<void>;
private _isDisposed: boolean = false;
@@ -40,8 +39,7 @@ export class TerminalProcess implements ITerminalChildProcess, IDisposable {
cols: number,
rows: number,
env: platform.IProcessEnvironment,
windowsEnableConpty: boolean,
@ILogService private readonly _logService: ILogService
windowsEnableConpty: boolean
) {
let shellName: string;
if (os.platform() === 'win32') {
@@ -71,42 +69,37 @@ export class TerminalProcess implements ITerminalChildProcess, IDisposable {
experimentalUseConpty: useConpty
};
// TODO: Need to verify whether executable is on $PATH, otherwise things like cmd.exe will break
// fs.stat(shellLaunchConfig.executable!, (err) => {
// if (err && err.code === 'ENOENT') {
// this._exitCode = SHELL_PATH_INVALID_EXIT_CODE;
// this._queueProcessExit();
// this._processStartupComplete = Promise.resolve(undefined);
// return;
// }
this.setupPtyProcess(shellLaunchConfig, options);
// });
}
private setupPtyProcess(shellLaunchConfig: IShellLaunchConfig, options: pty.IPtyForkOptions): void {
const args = shellLaunchConfig.args || [];
this._logService.trace('IPty#spawn', shellLaunchConfig.executable, args, options);
const ptyProcess = pty.spawn(shellLaunchConfig.executable!, args, options);
this._ptyProcess = ptyProcess;
this._processStartupComplete = new Promise<void>(c => {
this.onProcessIdReady(() => c());
});
ptyProcess.on('data', (data) => {
try {
this._ptyProcess = pty.spawn(shellLaunchConfig.executable!, shellLaunchConfig.args || [], options);
this._processStartupComplete = new Promise<void>(c => {
this.onProcessIdReady((pid) => {
c();
});
});
} catch (error) {
// The only time this is expected to happen is when the file specified to launch with does not exist.
this._exitCode = 2;
this._queueProcessExit();
this._processStartupComplete = Promise.resolve(undefined);
return;
}
this._ptyProcess.on('data', (data) => {
this._onProcessData.fire(data);
if (this._closeTimeout) {
clearTimeout(this._closeTimeout);
this._queueProcessExit();
}
});
ptyProcess.on('exit', (code) => {
this._ptyProcess.on('exit', (code) => {
this._exitCode = code;
this._queueProcessExit();
});
this._setupTitlePolling(ptyProcess);
// TODO: We should no longer need to delay this since pty.spawn is sync
setTimeout(() => {
this._sendProcessId(ptyProcess);
this._sendProcessId();
}, 500);
this._setupTitlePolling();
}
public dispose(): void {
@@ -121,15 +114,15 @@ export class TerminalProcess implements ITerminalChildProcess, IDisposable {
this._onProcessTitleChanged.dispose();
}
private _setupTitlePolling(ptyProcess: pty.IPty) {
private _setupTitlePolling() {
// Send initial timeout async to give event listeners a chance to init
setTimeout(() => {
this._sendProcessTitle(ptyProcess);
this._sendProcessTitle();
}, 0);
// Setup polling
this._titleInterval = setInterval(() => {
if (this._currentTitle !== ptyProcess.process) {
this._sendProcessTitle(ptyProcess);
if (this._currentTitle !== this._ptyProcess.process) {
this._sendProcessTitle();
}
}, 200);
}
@@ -153,10 +146,7 @@ export class TerminalProcess implements ITerminalChildProcess, IDisposable {
// Attempt to kill the pty, it may have already been killed at this
// point but we want to make sure
try {
if (this._ptyProcess) {
this._logService.trace('IPty#kill');
this._ptyProcess.kill();
}
this._ptyProcess.kill();
} catch (ex) {
// Swallow, the pty has already been killed
}
@@ -165,15 +155,15 @@ export class TerminalProcess implements ITerminalChildProcess, IDisposable {
});
}
private _sendProcessId(ptyProcess: pty.IPty) {
this._onProcessIdReady.fire(ptyProcess.pid);
private _sendProcessId() {
this._onProcessIdReady.fire(this._ptyProcess.pid);
}
private _sendProcessTitle(ptyProcess: pty.IPty): void {
private _sendProcessTitle(): void {
if (this._isDisposed) {
return;
}
this._currentTitle = ptyProcess.process;
this._currentTitle = this._ptyProcess.process;
this._onProcessTitleChanged.fire(this._currentTitle);
}
@@ -186,10 +176,9 @@ export class TerminalProcess implements ITerminalChildProcess, IDisposable {
}
public input(data: string): void {
if (this._isDisposed || !this._ptyProcess) {
if (this._isDisposed) {
return;
}
this._logService.trace('IPty#write', data);
this._ptyProcess.write(data);
}
@@ -199,12 +188,7 @@ export class TerminalProcess implements ITerminalChildProcess, IDisposable {
}
// Ensure that cols and rows are always >= 1, this prevents a native
// exception in winpty.
if (this._ptyProcess) {
cols = Math.max(cols, 1);
rows = Math.max(rows, 1);
this._logService.trace('IPty#resize', cols, rows);
this._ptyProcess.resize(cols, rows);
}
this._ptyProcess.resize(Math.max(cols, 1), Math.max(rows, 1));
}
public getInitialCwd(): Promise<string> {
@@ -214,11 +198,6 @@ export class TerminalProcess implements ITerminalChildProcess, IDisposable {
public getCwd(): Promise<string> {
if (platform.isMacintosh) {
return new Promise<string>(resolve => {
if (!this._ptyProcess) {
resolve(this._initialCwd);
return;
}
this._logService.trace('IPty#pid');
exec('lsof -p ' + this._ptyProcess.pid + ' | grep cwd', (error, stdout, stderr) => {
if (stdout !== '') {
resolve(stdout.substring(stdout.indexOf('/'), stdout.length - 1));
@@ -229,11 +208,6 @@ export class TerminalProcess implements ITerminalChildProcess, IDisposable {
if (platform.isLinux) {
return new Promise<string>(resolve => {
if (!this._ptyProcess) {
resolve(this._initialCwd);
return;
}
this._logService.trace('IPty#pid');
fs.readlink('/proc/' + this._ptyProcess.pid + '/cwd', (err, linkedstr) => {
if (err) {
resolve(this._initialCwd);

View File

@@ -61,15 +61,15 @@ export class WindowsShellHelper implements IWindowsShellHelper {
// 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(() => {
this._xterm.on('linefeed', () => this._newLineFeed = true);
this._xterm.on('cursormove', () => {
if (this._newLineFeed) {
this._onCheckShell.fire(undefined);
}
});
// Fire a new check for the shell when any key is pressed.
this._xterm.onKey(() => this._onCheckShell.fire(undefined));
this._xterm.on('keypress', () => this._onCheckShell.fire(undefined));
});
}