commit
27c5f6444e
|
@ -425,9 +425,9 @@
|
|||
"resolved": "https://registry.npmjs.org/winreg/-/winreg-1.2.0.tgz"
|
||||
},
|
||||
"xterm": {
|
||||
"version": "2.2.3",
|
||||
"version": "2.3.0",
|
||||
"from": "Tyriar/xterm.js#vscode-release/1.10",
|
||||
"resolved": "git+https://github.com/Tyriar/xterm.js.git#90cd66bf353b86ad52d7b650760d8d879dd1c7b8"
|
||||
"resolved": "git+https://github.com/Tyriar/xterm.js.git#5513303451202b0135601a2f026602ed391b3906"
|
||||
},
|
||||
"yauzl": {
|
||||
"version": "2.3.1",
|
||||
|
|
|
@ -58,6 +58,16 @@
|
|||
opacity: 0 !important;
|
||||
}
|
||||
|
||||
.monaco-workbench .panel.integrated-terminal .xterm a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.monaco-workbench .panel.integrated-terminal .xterm a:hover {
|
||||
cursor: pointer;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.monaco-workbench .panel.integrated-terminal .xterm:not(.xterm-cursor-style-underline):not(.xterm-cursor-style-bar).focus .reverse-video,
|
||||
.monaco-workbench .panel.integrated-terminal .xterm:not(.xterm-cursor-style-underline):not(.xterm-cursor-style-bar):focus .reverse-video { color: #CCC; }
|
||||
.vs-dark .monaco-workbench .panel.integrated-terminal .xterm:not(.xterm-cursor-style-underline):not(.xterm-cursor-style-bar).focus .reverse-video,
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as path from 'path';
|
||||
import DOM = require('vs/base/browser/dom');
|
||||
import Event, { Emitter } from 'vs/base/common/event';
|
||||
import URI from 'vs/base/common/uri';
|
||||
|
@ -10,7 +11,6 @@ import cp = require('child_process');
|
|||
import lifecycle = require('vs/base/common/lifecycle');
|
||||
import nls = require('vs/nls');
|
||||
import os = require('os');
|
||||
import path = require('path');
|
||||
import platform = require('vs/base/common/platform');
|
||||
import xterm = require('xterm');
|
||||
import { Dimension } from 'vs/base/browser/builder';
|
||||
|
@ -22,9 +22,11 @@ import { IStringDictionary } from 'vs/base/common/collections';
|
|||
import { ITerminalInstance, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, TERMINAL_PANEL_ID, IShellLaunchConfig } from 'vs/workbench/parts/terminal/common/terminal';
|
||||
import { ITerminalProcessFactory } from 'vs/workbench/parts/terminal/electron-browser/terminal';
|
||||
import { IWorkspace, IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
import { TabFocus } from 'vs/editor/common/config/commonEditorConfig';
|
||||
import { TerminalConfigHelper } from 'vs/workbench/parts/terminal/electron-browser/terminalConfigHelper';
|
||||
import { TerminalLinkHandler } from 'vs/workbench/parts/terminal/electron-browser/terminalLinkHandler';
|
||||
|
||||
/** The amount of time to consider terminal errors to be related to the launch */
|
||||
const LAUNCHING_DURATION = 500;
|
||||
|
@ -74,13 +76,15 @@ export class TerminalInstance implements ITerminalInstance {
|
|||
public constructor(
|
||||
private _terminalFocusContextKey: IContextKey<boolean>,
|
||||
private _configHelper: TerminalConfigHelper,
|
||||
private _linkHandler: TerminalLinkHandler,
|
||||
private _container: HTMLElement,
|
||||
private _shellLaunchConfig: IShellLaunchConfig,
|
||||
@IContextKeyService private _contextKeyService: IContextKeyService,
|
||||
@IKeybindingService private _keybindingService: IKeybindingService,
|
||||
@IMessageService private _messageService: IMessageService,
|
||||
@IPanelService private _panelService: IPanelService,
|
||||
@IWorkspaceContextService private _contextService: IWorkspaceContextService
|
||||
@IWorkspaceContextService private _contextService: IWorkspaceContextService,
|
||||
@IWorkbenchEditorService private _editorService: IWorkbenchEditorService
|
||||
) {
|
||||
this._instanceDisposables = [];
|
||||
this._processDisposables = [];
|
||||
|
@ -139,6 +143,7 @@ export class TerminalInstance implements ITerminalInstance {
|
|||
this._xtermElement = document.createElement('div');
|
||||
|
||||
this._xterm.open(this._xtermElement);
|
||||
this._xterm.registerLinkMatcher(this._linkHandler.localLinkRegex, (url) => this._linkHandler.handleLocalLink(url), 1);
|
||||
this._xterm.attachCustomKeydownHandler((event: KeyboardEvent) => {
|
||||
// Disable all input if the terminal is exiting
|
||||
if (this._isExiting) {
|
||||
|
|
|
@ -0,0 +1,92 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as path from 'path';
|
||||
import * as pfs from 'vs/base/node/pfs';
|
||||
import Uri from 'vs/base/common/uri';
|
||||
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
import { Platform } from 'vs/base/common/platform';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
|
||||
const pathPrefix = '(\\.\\.?|\\~)';
|
||||
const pathSeparatorClause = '\\/';
|
||||
const excludedPathCharactersClause = '[^\\0\\s!$`&*()+\'":;]'; // '":; are allowed in paths but they are often separators so ignore them
|
||||
const escapedExcludedPathCharactersClause = '(\\\\s|\\\\!|\\\\$|\\\\`|\\\\&|\\\\*|(|)|\\+)';
|
||||
/** A regex that matches paths in the form /path, ~/path, ./path, ../path */
|
||||
const UNIX_LIKE_LOCAL_LINK_REGEX = new RegExp('(' + pathPrefix + '?(' + pathSeparatorClause + '(' + excludedPathCharactersClause + '|' + escapedExcludedPathCharactersClause + ')+)+)');
|
||||
|
||||
const winPathPrefix = '([a-zA-Z]:|\\.\\.?|\\~)';
|
||||
const winPathSeparatorClause = '(\\\\|\\/)';
|
||||
const winExcludedPathCharactersClause = '[^\\0<>\\?\\|\\/\\s!$`&*()+\'":;]';
|
||||
/** A regex that matches paths in the form c:\path, ~\path, .\path */
|
||||
const WINDOWS_LOCAL_LINK_REGEX = new RegExp('(' + winPathPrefix + '?(' + winPathSeparatorClause + '(' + winExcludedPathCharactersClause + ')+)+)');
|
||||
|
||||
export class TerminalLinkHandler {
|
||||
constructor(
|
||||
private _platform: Platform,
|
||||
@IWorkbenchEditorService private _editorService: IWorkbenchEditorService,
|
||||
@IWorkspaceContextService private _contextService: IWorkspaceContextService
|
||||
) {
|
||||
}
|
||||
|
||||
public get localLinkRegex(): RegExp {
|
||||
if (this._platform === Platform.Windows) {
|
||||
return WINDOWS_LOCAL_LINK_REGEX;
|
||||
}
|
||||
return UNIX_LIKE_LOCAL_LINK_REGEX;
|
||||
}
|
||||
|
||||
public handleLocalLink(link: string): TPromise<void> {
|
||||
if (this._platform === Platform.Windows) {
|
||||
return this._handleWindowsLocalLink(link);
|
||||
}
|
||||
return this._handleUnixLikeLocalLink(link);
|
||||
}
|
||||
|
||||
private _handleUnixLikeLocalLink(link: string): TPromise<void> {
|
||||
// Resolve ~ -> $HOME
|
||||
if (link.charAt(0) === '~') {
|
||||
if (!process.env.HOME) {
|
||||
return TPromise.as(void 0);
|
||||
}
|
||||
link = process.env.HOME + link.substring(1);
|
||||
}
|
||||
return this._handleCommonLocalLink(link);
|
||||
}
|
||||
|
||||
private _handleWindowsLocalLink(link: string): TPromise<void> {
|
||||
// Resolve ~ -> %HOMEDRIVE%\%HOMEPATH%
|
||||
if (link.charAt(0) === '~') {
|
||||
if (!process.env.HOMEDRIVE || !process.env.HOMEPATH) {
|
||||
return TPromise.as(void 0);
|
||||
}
|
||||
link = `${process.env.HOMEDRIVE}\\${process.env.HOMEPATH + link.substring(1)}`;
|
||||
}
|
||||
return this._handleCommonLocalLink(link);
|
||||
}
|
||||
|
||||
private _handleCommonLocalLink(link: string): TPromise<void> {
|
||||
// Resolve workspace path . / .. -> <path>/. / <path/..
|
||||
if (link.charAt(0) === '.') {
|
||||
if (!this._contextService.hasWorkspace) {
|
||||
// Abort if no workspace is open
|
||||
return TPromise.as(void 0);
|
||||
}
|
||||
link = path.join(this._contextService.getWorkspace().resource.fsPath, link);
|
||||
}
|
||||
|
||||
// Clean up the path
|
||||
const resource = Uri.file(path.normalize(path.resolve(link)));
|
||||
|
||||
// Open an editor if the path exists
|
||||
return pfs.fileExists(link).then(isFile => {
|
||||
if (!isFile) {
|
||||
return void 0;
|
||||
}
|
||||
return this._editorService.openEditor({ resource }).then(() => void 0);
|
||||
});
|
||||
}
|
||||
}
|
|
@ -15,12 +15,14 @@ import { ITerminalInstance, ITerminalService, IShellLaunchConfig, KEYBINDING_CON
|
|||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { TerminalConfigHelper } from 'vs/workbench/parts/terminal/electron-browser/terminalConfigHelper';
|
||||
import { TerminalInstance } from 'vs/workbench/parts/terminal/electron-browser/terminalInstance';
|
||||
import { TerminalLinkHandler } from 'vs/workbench/parts/terminal/electron-browser/terminalLinkHandler';
|
||||
|
||||
export class TerminalService implements ITerminalService {
|
||||
public _serviceBrand: any;
|
||||
|
||||
private _activeTerminalInstanceIndex: number;
|
||||
private _configHelper: TerminalConfigHelper;
|
||||
private _linkHandler: TerminalLinkHandler;
|
||||
private _onActiveInstanceChanged: Emitter<string>;
|
||||
private _onInstanceDisposed: Emitter<ITerminalInstance>;
|
||||
private _onInstanceProcessIdReady: Emitter<ITerminalInstance>;
|
||||
|
@ -57,7 +59,8 @@ export class TerminalService implements ITerminalService {
|
|||
|
||||
this._configurationService.onDidUpdateConfiguration(() => this.updateConfig());
|
||||
this._terminalFocusContextKey = KEYBINDING_CONTEXT_TERMINAL_FOCUS.bindTo(this._contextKeyService);
|
||||
this._configHelper = <TerminalConfigHelper>this._instantiationService.createInstance(TerminalConfigHelper, platform.platform);
|
||||
this._configHelper = this._instantiationService.createInstance(TerminalConfigHelper, platform.platform);
|
||||
this._linkHandler = this._instantiationService.createInstance(TerminalLinkHandler, platform.platform);
|
||||
this.onInstanceDisposed((terminalInstance) => { this._removeInstance(terminalInstance); });
|
||||
}
|
||||
|
||||
|
@ -65,6 +68,7 @@ export class TerminalService implements ITerminalService {
|
|||
let terminalInstance = this._instantiationService.createInstance(TerminalInstance,
|
||||
this._terminalFocusContextKey,
|
||||
this._configHelper,
|
||||
this._linkHandler,
|
||||
this._terminalContainer,
|
||||
shell);
|
||||
terminalInstance.addDisposable(terminalInstance.onTitleChanged(this._onInstanceTitleChanged.fire, this._onInstanceTitleChanged));
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { Platform } from 'vs/base/common/platform';
|
||||
import { TerminalLinkHandler } from 'vs/workbench/parts/terminal/electron-browser/terminalLinkHandler';
|
||||
|
||||
suite('Workbench - TerminalLinkHandler', () => {
|
||||
suite('localLinkRegex', () => {
|
||||
test('Windows', () => {
|
||||
const regex = new TerminalLinkHandler(Platform.Windows, null, null).localLinkRegex;
|
||||
function testLink(link: string) {
|
||||
assert.equal(` ${link} `.match(regex)[1], link);
|
||||
assert.equal(`:${link}:`.match(regex)[1], link);
|
||||
assert.equal(`;${link};`.match(regex)[1], link);
|
||||
assert.equal(`(${link})`.match(regex)[1], link);
|
||||
}
|
||||
testLink('c:\\foo');
|
||||
testLink('c:/foo');
|
||||
testLink('.\\foo');
|
||||
testLink('./foo');
|
||||
testLink('..\\foo');
|
||||
testLink('../foo');
|
||||
testLink('~\\foo');
|
||||
testLink('~/foo');
|
||||
testLink('c:/a/long/path');
|
||||
testLink('c:\\a\\long\\path');
|
||||
testLink('c:\\mixed/slash\\path');
|
||||
});
|
||||
|
||||
test('Linux', () => {
|
||||
const regex = new TerminalLinkHandler(Platform.Linux, null, null).localLinkRegex;
|
||||
function testLink(link: string) {
|
||||
assert.equal(` ${link} `.match(regex)[1], link);
|
||||
assert.equal(`:${link}:`.match(regex)[1], link);
|
||||
assert.equal(`;${link};`.match(regex)[1], link);
|
||||
assert.equal(`(${link})`.match(regex)[1], link);
|
||||
}
|
||||
testLink('/foo');
|
||||
testLink('~/foo');
|
||||
testLink('./foo');
|
||||
testLink('../foo');
|
||||
testLink('/a/long/path');
|
||||
});
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue