提交 c09c79f0 编写于 作者: M Michel Kaporin

Added multiple link detection for debug console and exception widget.

上级 8281fb30
...@@ -14,6 +14,8 @@ import { RunOnceScheduler } from 'vs/base/common/async'; ...@@ -14,6 +14,8 @@ import { RunOnceScheduler } from 'vs/base/common/async';
import { IThemeService, ITheme } from "vs/platform/theme/common/themeService"; import { IThemeService, ITheme } from "vs/platform/theme/common/themeService";
import { Color } from "vs/base/common/color"; import { Color } from "vs/base/common/color";
import { registerColor } from "vs/platform/theme/common/colorRegistry"; import { registerColor } from "vs/platform/theme/common/colorRegistry";
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { LinkDetector } from 'vs/workbench/parts/debug/browser/linkDetector';
const $ = dom.$; const $ = dom.$;
// theming // theming
...@@ -28,7 +30,8 @@ export class ExceptionWidget extends ZoneWidget { ...@@ -28,7 +30,8 @@ export class ExceptionWidget extends ZoneWidget {
constructor(editor: ICodeEditor, private exceptionInfo: IExceptionInfo, private lineNumber: number, constructor(editor: ICodeEditor, private exceptionInfo: IExceptionInfo, private lineNumber: number,
@IContextViewService private contextViewService: IContextViewService, @IContextViewService private contextViewService: IContextViewService,
@IDebugService private debugService: IDebugService, @IDebugService private debugService: IDebugService,
@IThemeService themeService: IThemeService @IThemeService themeService: IThemeService,
@IInstantiationService private instantiationService: IInstantiationService
) { ) {
super(editor, { showFrame: true, showArrow: true, frameWidth: 1 }); super(editor, { showFrame: true, showArrow: true, frameWidth: 1 });
...@@ -95,7 +98,9 @@ export class ExceptionWidget extends ZoneWidget { ...@@ -95,7 +98,9 @@ export class ExceptionWidget extends ZoneWidget {
if (this.exceptionInfo.details && this.exceptionInfo.details.stackTrace) { if (this.exceptionInfo.details && this.exceptionInfo.details.stackTrace) {
let stackTrace = $('.stack-trace'); let stackTrace = $('.stack-trace');
stackTrace.textContent = this.exceptionInfo.details.stackTrace; const linkDetector = this.instantiationService.createInstance(LinkDetector);
const linkedStackTrace = linkDetector.handleLinks(this.exceptionInfo.details.stackTrace);
typeof linkedStackTrace === 'string' ? stackTrace.textContent = linkedStackTrace : stackTrace.appendChild(linkedStackTrace);
dom.append(container, stackTrace); dom.append(container, stackTrace);
} }
} }
......
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import uri from 'vs/base/common/uri';
import { isMacintosh } from 'vs/base/common/platform';
import * as errors from 'vs/base/common/errors';
import { IMouseEvent, StandardMouseEvent } from 'vs/base/browser/mouseEvent';
import * as nls from 'vs/nls';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
export class LinkDetector {
private static FILE_LOCATION_PATTERNS: RegExp[] = [
// group 0: the full thing :)
// group 1: absolute path
// group 2: drive letter on windows with trailing backslash or leading slash on mac/linux
// group 3: line number
// group 4: column number
// eg: at Context.<anonymous> (c:\Users\someone\Desktop\mocha-runner\test\test.js:26:11)
/((\/|[a-zA-Z]:\\)[^\(\)<>\'\"\[\]]+):(\d+):(\d+)/g
];
constructor(
@IWorkbenchEditorService private editorService: IWorkbenchEditorService
) {
// noop
}
public handleLinks(text: string): HTMLElement | string {
let linkContainer: HTMLElement;
for (let pattern of LinkDetector.FILE_LOCATION_PATTERNS) {
pattern.lastIndex = 0; // the holy grail of software development
let lastMatchIndex = 0;
let match = pattern.exec(text);
while (match !== null) {
let resource: uri = null;
try {
resource = match && uri.file(match[1]);
} catch (e) { }
if (resource) {
if (!linkContainer) {
linkContainer = document.createElement('span');
}
let textBeforeLink = text.substring(lastMatchIndex, match.index);
if (textBeforeLink) {
let span = document.createElement('span');
span.textContent = textBeforeLink;
linkContainer.appendChild(span);
}
const link = document.createElement('a');
link.textContent = text.substr(match.index, match[0].length);
link.title = isMacintosh ? nls.localize('fileLinkMac', "Click to follow (Cmd + click opens to the side)") : nls.localize('fileLink', "Click to follow (Ctrl + click opens to the side)");
linkContainer.appendChild(link);
const line = Number(match[3]);
const column = Number(match[4]);
link.onclick = (e) => this.onLinkClick(new StandardMouseEvent(e), resource, line, column);
lastMatchIndex = pattern.lastIndex;
const previousMatch = match;
match = pattern.exec(text);
// Append remaining text if no more links detected
if (!match) {
let textAfterLink = text.substr(previousMatch.index + previousMatch[0].length);
if (textAfterLink) {
let span = document.createElement('span');
span.textContent = textAfterLink;
linkContainer.appendChild(span);
}
}
}
}
}
return linkContainer || text;
}
private onLinkClick(event: IMouseEvent, resource: uri, line: number, column: number): void {
const selection = window.getSelection();
if (selection.type === 'Range') {
return; // do not navigate when user is selecting
}
event.preventDefault();
this.editorService.openEditor({
resource,
options: {
selection: {
startLineNumber: line,
startColumn: column
}
}
}, event.ctrlKey || event.metaKey).done(null, errors.onUnexpectedError);
}
}
...@@ -27,6 +27,11 @@ ...@@ -27,6 +27,11 @@
margin-top: 0.5em; margin-top: 0.5em;
} }
.monaco-editor .zone-widget .zone-widget-container.exception-widget a {
text-decoration: underline;
cursor: pointer;
}
/* High Contrast Theming */ /* High Contrast Theming */
.monaco-workbench.mac .zone-widget .zone-widget-container.exception-widget { .monaco-workbench.mac .zone-widget .zone-widget-container.exception-widget {
......
...@@ -7,13 +7,10 @@ import * as nls from 'vs/nls'; ...@@ -7,13 +7,10 @@ import * as nls from 'vs/nls';
import { TPromise } from 'vs/base/common/winjs.base'; import { TPromise } from 'vs/base/common/winjs.base';
import { IAction } from 'vs/base/common/actions'; import { IAction } from 'vs/base/common/actions';
import { isFullWidthCharacter, removeAnsiEscapeCodes, endsWith } from 'vs/base/common/strings'; import { isFullWidthCharacter, removeAnsiEscapeCodes, endsWith } from 'vs/base/common/strings';
import uri from 'vs/base/common/uri';
import { isMacintosh } from 'vs/base/common/platform';
import { IActionItem } from 'vs/base/browser/ui/actionbar/actionbar'; import { IActionItem } from 'vs/base/browser/ui/actionbar/actionbar';
import * as dom from 'vs/base/browser/dom'; import * as dom from 'vs/base/browser/dom';
import * as errors from 'vs/base/common/errors';
import severity from 'vs/base/common/severity'; import severity from 'vs/base/common/severity';
import { IMouseEvent, StandardMouseEvent } from 'vs/base/browser/mouseEvent'; import { IMouseEvent } from 'vs/base/browser/mouseEvent';
import { ITree, IAccessibilityProvider, IDataSource, IRenderer, IActionProvider } from 'vs/base/parts/tree/browser/tree'; import { ITree, IAccessibilityProvider, IDataSource, IRenderer, IActionProvider } from 'vs/base/parts/tree/browser/tree';
import { ICancelableEvent } from 'vs/base/parts/tree/browser/treeDefaults'; import { ICancelableEvent } from 'vs/base/parts/tree/browser/treeDefaults';
import { IExpressionContainer, IExpression } from 'vs/workbench/parts/debug/common/debug'; import { IExpressionContainer, IExpression } from 'vs/workbench/parts/debug/common/debug';
...@@ -23,6 +20,7 @@ import { ClearReplAction } from 'vs/workbench/parts/debug/browser/debugActions'; ...@@ -23,6 +20,7 @@ import { ClearReplAction } from 'vs/workbench/parts/debug/browser/debugActions';
import { CopyAction } from 'vs/workbench/parts/debug/electron-browser/electronDebugActions'; import { CopyAction } from 'vs/workbench/parts/debug/electron-browser/electronDebugActions';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import { LinkDetector } from 'vs/workbench/parts/debug/browser/linkDetector';
const $ = dom.$; const $ = dom.$;
...@@ -83,23 +81,14 @@ export class ReplExpressionsRenderer implements IRenderer { ...@@ -83,23 +81,14 @@ export class ReplExpressionsRenderer implements IRenderer {
private static VALUE_OUTPUT_TEMPLATE_ID = 'outputValue'; private static VALUE_OUTPUT_TEMPLATE_ID = 'outputValue';
private static NAME_VALUE_OUTPUT_TEMPLATE_ID = 'outputNameValue'; private static NAME_VALUE_OUTPUT_TEMPLATE_ID = 'outputNameValue';
private static FILE_LOCATION_PATTERNS: RegExp[] = [
// group 0: the full thing :)
// group 1: absolute path
// group 2: drive letter on windows with trailing backslash or leading slash on mac/linux
// group 3: line number
// group 4: column number
// eg: at Context.<anonymous> (c:\Users\someone\Desktop\mocha-runner\test\test.js:26:11)
/((\/|[a-zA-Z]:\\)[^\(\)<>\'\"\[\]]+):(\d+):(\d+)/
];
private static LINE_HEIGHT_PX = 18; private static LINE_HEIGHT_PX = 18;
private width: number; private width: number;
private characterWidth: number; private characterWidth: number;
constructor( constructor(
@IWorkbenchEditorService private editorService: IWorkbenchEditorService @IWorkbenchEditorService private editorService: IWorkbenchEditorService,
@IInstantiationService private instantiationService: IInstantiationService
) { ) {
// noop // noop
} }
...@@ -330,7 +319,8 @@ export class ReplExpressionsRenderer implements IRenderer { ...@@ -330,7 +319,8 @@ export class ReplExpressionsRenderer implements IRenderer {
// flush text buffer if we have any // flush text buffer if we have any
if (buffer) { if (buffer) {
this.insert(this.handleLinks(buffer), currentToken || tokensContainer); const linkDetector = this.instantiationService.createInstance(LinkDetector);
this.insert(linkDetector.handleLinks(buffer), currentToken || tokensContainer);
buffer = ''; buffer = '';
} }
...@@ -350,7 +340,8 @@ export class ReplExpressionsRenderer implements IRenderer { ...@@ -350,7 +340,8 @@ export class ReplExpressionsRenderer implements IRenderer {
// flush remaining text buffer if we have any // flush remaining text buffer if we have any
if (buffer) { if (buffer) {
let res = this.handleLinks(buffer); const linkDetector = this.instantiationService.createInstance(LinkDetector);
let res = linkDetector.handleLinks(buffer);
if (typeof res !== 'string' || currentToken) { if (typeof res !== 'string' || currentToken) {
if (!tokensContainer) { if (!tokensContainer) {
tokensContainer = document.createElement('span'); tokensContainer = document.createElement('span');
...@@ -371,67 +362,6 @@ export class ReplExpressionsRenderer implements IRenderer { ...@@ -371,67 +362,6 @@ export class ReplExpressionsRenderer implements IRenderer {
} }
} }
private handleLinks(text: string): HTMLElement | string {
let linkContainer: HTMLElement;
for (let pattern of ReplExpressionsRenderer.FILE_LOCATION_PATTERNS) {
pattern.lastIndex = 0; // the holy grail of software development
const match = pattern.exec(text);
let resource: uri = null;
try {
resource = match && uri.file(match[1]);
} catch (e) { }
if (resource) {
linkContainer = document.createElement('span');
let textBeforeLink = text.substr(0, match.index);
if (textBeforeLink) {
let span = document.createElement('span');
span.textContent = textBeforeLink;
linkContainer.appendChild(span);
}
const link = document.createElement('a');
link.textContent = text.substr(match.index, match[0].length);
link.title = isMacintosh ? nls.localize('fileLinkMac', "Click to follow (Cmd + click opens to the side)") : nls.localize('fileLink', "Click to follow (Ctrl + click opens to the side)");
linkContainer.appendChild(link);
link.onclick = (e) => this.onLinkClick(new StandardMouseEvent(e), resource, Number(match[3]), Number(match[4]));
let textAfterLink = text.substr(match.index + match[0].length);
if (textAfterLink) {
let span = document.createElement('span');
span.textContent = textAfterLink;
linkContainer.appendChild(span);
}
break; // support one link per line for now
}
}
return linkContainer || text;
}
private onLinkClick(event: IMouseEvent, resource: uri, line: number, column: number): void {
const selection = window.getSelection();
if (selection.type === 'Range') {
return; // do not navigate when user is selecting
}
event.preventDefault();
this.editorService.openEditor({
resource,
options: {
selection: {
startLineNumber: line,
startColumn: column
}
}
}, event.ctrlKey || event.metaKey).done(null, errors.onUnexpectedError);
}
public disposeTemplate(tree: ITree, templateId: string, templateData: any): void { public disposeTemplate(tree: ITree, templateId: string, templateData: any): void {
// noop // noop
} }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册