提交 7d4e15a3 编写于 作者: D Daniel Imms 提交者: GitHub

Merge pull request #30732 from Microsoft/amqi/update-window-shellname

Rename terminal instance on enter in Windows
......@@ -361,5 +361,5 @@ export interface ITerminalInstance {
/**
* Sets the title of the terminal instance.
*/
setTitle(title: string): void;
setTitle(title: string, eventFromProcess: boolean): void;
}
......@@ -622,7 +622,7 @@ export class RenameTerminalAction extends Action {
prompt: nls.localize('workbench.action.terminal.rename.prompt', "Enter terminal name"),
}).then(name => {
if (name) {
terminalInstance.setTitle(name);
terminalInstance.setTitle(name, false);
}
});
}
......
......@@ -12,6 +12,7 @@ import * as platform from 'vs/base/common/platform';
import * as dom from 'vs/base/browser/dom';
import Event, { Emitter } from 'vs/base/common/event';
import Uri from 'vs/base/common/uri';
import { WindowsShellHelper } from 'vs/workbench/parts/terminal/electron-browser/windowsShellHelper';
import XTermTerminal = require('xterm');
import { Dimension } from 'vs/base/browser/builder';
import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
......@@ -84,6 +85,7 @@ export class TerminalInstance implements ITerminalInstance {
private _messageTitleListener: (message: { type: string, content: string }) => void;
private _preLaunchInputQueue: string;
private _initialCwd: string;
private _windowsShellHelper: WindowsShellHelper;
private _widgetManager: TerminalWidgetManager;
private _linkHandler: TerminalLinkHandler;
......@@ -138,6 +140,12 @@ export class TerminalInstance implements ITerminalInstance {
this._createProcess(this._shellLaunchConfig);
this._createXterm();
if (platform.isWindows) {
this._processReady.then(() => {
this._windowsShellHelper = new WindowsShellHelper(this._processId, this._shellLaunchConfig.executable);
});
}
// Only attach xterm.js to the DOM if the terminal panel has been opened before.
if (_container) {
this.attachToElement(_container);
......@@ -264,6 +272,13 @@ export class TerminalInstance implements ITerminalInstance {
if (TabFocus.getTabFocusMode() && event.keyCode === 9) {
return false;
}
// Windows does not get a process title event from terminalProcess so we check the name on enter
// messageTitleListener is falsy when the API/user renames the terminal so we don't override it
if (platform.isWindows && event.keyCode === 13 /* ENTER */ && this._messageTitleListener) {
this._windowsShellHelper.getShellName().then(title => this.setTitle(title, true));
}
return undefined;
});
this._instanceDisposables.push(dom.addDisposableListener(this._xterm.element, 'mouseup', (event: KeyboardEvent) => {
......@@ -519,17 +534,17 @@ export class TerminalInstance implements ITerminalInstance {
const platformKey = platform.isWindows ? 'windows' : platform.isMacintosh ? 'osx' : 'linux';
const envFromConfig = { ...process.env, ...this._configHelper.config.env[platformKey] };
const env = TerminalInstance.createTerminalEnv(envFromConfig, shell, this._initialCwd, locale, this._cols, this._rows);
this._title = shell.name || '';
this._process = cp.fork(Uri.parse(require.toUrl('bootstrap')).fsPath, ['--type=terminal'], {
env,
cwd: Uri.parse(path.dirname(require.toUrl('../node/terminalProcess'))).fsPath
});
if (!shell.name) {
if (shell.name) {
this.setTitle(shell.name, false);
} else {
// Only listen for process title changes when a name is not provided
this._messageTitleListener = (message) => {
if (message.type === 'title') {
this._title = message.content ? message.content : '';
this._onTitleChanged.fire(this._title);
this.setTitle(message.content ? message.content : '', true);
}
};
this._process.on('message', this._messageTitleListener);
......@@ -652,7 +667,7 @@ export class TerminalInstance implements ITerminalInstance {
const oldTitle = this._title;
this._createProcess(shell);
if (oldTitle !== this._title) {
this._onTitleChanged.fire(this._title);
this.setTitle(this._title, true);
}
this._process.on('message', (message) => this._sendPtyDataToXterm(message));
......@@ -829,19 +844,25 @@ export class TerminalInstance implements ITerminalInstance {
this._terminalProcessFactory = factory;
}
public setTitle(title: string): void {
public setTitle(title: string, eventFromProcess: boolean): void {
if (eventFromProcess) {
if (platform.isWindows) {
// Remove the .exe extension
title = path.basename(title.split('.exe')[0]);
}
} else {
// If the title has not been set by the API or the rename command, unregister the handler that
// automatically updates the terminal name
if (this._process && this._messageTitleListener) {
this._process.removeListener('message', this._messageTitleListener);
this._messageTitleListener = null;
}
}
const didTitleChange = title !== this._title;
this._title = title;
if (didTitleChange) {
this._onTitleChanged.fire(title);
}
// If the title was not set by the API, unregister the handler that
// automatically updates the terminal name
if (this._process && this._messageTitleListener) {
this._process.removeListener('message', this._messageTitleListener);
this._messageTitleListener = null;
}
}
}
......@@ -866,4 +887,4 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
if (scrollbarSliderActiveBackgroundColor) {
collector.addRule(`.monaco-workbench .panel.integrated-terminal .xterm .xterm-viewport::-webkit-scrollbar-thumb:active { background-color: ${scrollbarSliderActiveBackgroundColor}; }`);
}
});
\ No newline at end of file
});
......@@ -3,6 +3,8 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as dom from 'vs/base/browser/dom';
import * as nls from 'vs/nls';
import * as platform from 'vs/base/common/platform';
......
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as cp from 'child_process';
import * as platform from 'vs/base/common/platform';
import * as path from 'path';
import { TPromise } from 'vs/base/common/winjs.base';
import { Emitter, debounceEvent } from 'vs/base/common/event';
const SHELL_EXECUTABLES = ['cmd.exe', 'powershell.exe', 'bash.exe'];
export class WindowsShellHelper {
private _childProcessIdStack: number[];
private _onCheckWindowsShell: Emitter<string>;
private _rootShellExecutable: string;
private _rootProcessId: number;
public constructor(rootProcessId: number, rootShellExecutable: string) {
this._childProcessIdStack = [];
this._rootShellExecutable = rootShellExecutable;
this._rootProcessId = rootProcessId;
if (!platform.isWindows) {
throw new Error(`WindowsShellHelper cannot be instantiated on ${platform.platform}`);
}
this._onCheckWindowsShell = new Emitter<string>();
debounceEvent(this._onCheckWindowsShell.event, (l, e) => e, 100, true)(() => {
this.getShellName();
});
}
private getChildProcessDetails(pid: number): TPromise<{ executable: string, pid: number }[]> {
return new TPromise((resolve, reject) => {
cp.execFile('wmic.exe', ['process', 'where', `parentProcessId=${pid}`, 'get', 'ExecutablePath,ProcessId'], (err, stdout, stderr) => {
if (err) {
reject(err);
} else if (stderr.length > 0) {
resolve([]); // No processes found
} else {
const childProcessLines = stdout.split('\n').slice(1).filter(str => !/^\s*$/.test(str));
const childProcessDetails = childProcessLines.map(str => {
const s = str.split(' ');
return { executable: s[0], pid: Number(s[1]) };
});
resolve(childProcessDetails);
}
});
});
}
private refreshShellProcessTree(pid: number, parent: string): TPromise<string> {
return this.getChildProcessDetails(pid).then(result => {
// When we didn't find any child processes of the process
if (result.length === 0) {
// Case where we found a child process already and are checking further down the pid tree
// We have reached the end here so we know that parent is the deepest first child of the tree
if (parent) {
return TPromise.as(parent);
}
// Case where we haven't found a child and only the root shell is left
if (this._childProcessIdStack.length === 1) {
return TPromise.as(this._rootShellExecutable);
}
// Otherwise, we go up the tree to find the next valid deepest child of the root
this._childProcessIdStack.pop();
return this.refreshShellProcessTree(this._childProcessIdStack[this._childProcessIdStack.length - 1], null);
}
// We only go one level deep when checking for children of processes other then shells
if (SHELL_EXECUTABLES.indexOf(path.basename(result[0].executable)) === -1) {
return TPromise.as(result[0].executable);
}
// Save the pid in the stack and keep looking for children of that child
this._childProcessIdStack.push(result[0].pid);
return this.refreshShellProcessTree(result[0].pid, result[0].executable);
}, error => { return error; });
}
/**
* Returns the innermost shell executable running in the terminal
*/
public getShellName(): TPromise<string> {
if (this._childProcessIdStack.length === 0) {
this._childProcessIdStack.push(this._rootProcessId);
}
return new TPromise<string>((resolve) => {
this.refreshShellProcessTree(this._childProcessIdStack[this._childProcessIdStack.length - 1], null).then(result => {
resolve(result);
}, error => { return error; });
});
}
}
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册