提交 5a698788 编写于 作者: D Daniel Imms

Implement terminal link validation

Fixes #21226
上级 9e8f96c5
......@@ -60,6 +60,11 @@
text-decoration: underline;
}
.monaco-workbench .panel.integrated-terminal .xterm a.xterm-invalid-link:hover {
cursor: text;
text-decoration: none;
}
.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,
......
......@@ -196,7 +196,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._linkHandler.registerLocalLinkHandler(this._xterm);
this._xterm.attachCustomKeydownHandler((event: KeyboardEvent) => {
// Disable all input if the terminal is exiting
if (this._isExiting) {
......
......@@ -32,43 +32,56 @@ export class TerminalLinkHandler {
) {
}
public get localLinkRegex(): RegExp {
public registerLocalLinkHandler(xterm: any) {
xterm.registerLinkMatcher(this._localLinkRegex, (url) => this._handleLocalLink(url), {
matchIndex: 1,
validationCallback: (link: string, callback: (isValid: boolean) => void) => this._validateLocalLink(link, callback)
});
}
protected 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 _handleLocalLink(link: string): TPromise<void> {
return this._resolvePath(link).then(resolvedLink => {
if (!resolvedLink) {
return void 0;
}
const resource = Uri.file(path.normalize(path.resolve(resolvedLink)));
return this._editorService.openEditor({ resource }).then(() => void 0);
});
}
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 _validateLocalLink(link: string, callback: (isValid: boolean) => void): void {
console.log('validate');
this._resolvePath(link).then(resolvedLink => {
callback(!!resolvedLink);
});
}
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);
private _resolvePath(link: string): TPromise<string> {
if (this._platform === Platform.Windows) {
// 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)}`;
}
} else {
// 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);
}
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) {
......@@ -78,15 +91,12 @@ export class TerminalLinkHandler {
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 null;
}
return this._editorService.openEditor({ resource }).then(() => void 0);
return link;
});
}
}
......@@ -9,10 +9,16 @@ import * as assert from 'assert';
import { Platform } from 'vs/base/common/platform';
import { TerminalLinkHandler } from 'vs/workbench/parts/terminal/electron-browser/terminalLinkHandler';
class TestTerminalLinkHandler extends TerminalLinkHandler {
public get localLinkRegex(): RegExp {
return this._localLinkRegex;
}
}
suite('Workbench - TerminalLinkHandler', () => {
suite('localLinkRegex', () => {
test('Windows', () => {
const regex = new TerminalLinkHandler(Platform.Windows, null, null).localLinkRegex;
const regex = new TestTerminalLinkHandler(Platform.Windows, null, null).localLinkRegex;
function testLink(link: string) {
assert.equal(` ${link} `.match(regex)[1], link);
assert.equal(`:${link}:`.match(regex)[1], link);
......@@ -33,7 +39,7 @@ suite('Workbench - TerminalLinkHandler', () => {
});
test('Linux', () => {
const regex = new TerminalLinkHandler(Platform.Linux, null, null).localLinkRegex;
const regex = new TestTerminalLinkHandler(Platform.Linux, null, null).localLinkRegex;
function testLink(link: string) {
assert.equal(` ${link} `.match(regex)[1], link);
assert.equal(`:${link}:`.match(regex)[1], link);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册