提交 253d255f 编写于 作者: I isidor

debug: location of repl messages

fixes #24490
上级 88449158
......@@ -36,6 +36,22 @@
font-size: 12px;
}
.monaco-workbench.mac .repl .repl-tree .monaco-tree-row .output.expression.value-and-source {
display: flex;
}
.monaco-workbench.mac .repl .repl-tree .monaco-tree-row .output.expression.value-and-source .value {
flex: 1;
}
.monaco-workbench.mac .repl .repl-tree .monaco-tree-row .output.expression.value-and-source .source {
margin-left: 4px;
margin-right: 8px;
cursor: pointer;
text-decoration: underline;
}
.monaco-workbench.windows .repl .repl-tree .monaco-tree-row .input.expression,
.monaco-workbench.windows .repl .repl-tree .monaco-tree-row .output.expression,
.monaco-workbench.linux .repl .repl-tree .monaco-tree-row .input.expression,
......
......@@ -73,6 +73,13 @@ export interface ITreeElement {
export interface IReplElement extends ITreeElement {
toString(): string;
sourceData?: IReplElementSource;
}
export interface IReplElementSource {
source: Source;
lineNumber: number;
column: number;
}
export interface IExpressionContainer extends ITreeElement {
......
......@@ -19,7 +19,7 @@ import { Range, IRange } from 'vs/editor/common/core/range';
import { ISuggestion } from 'vs/editor/common/modes';
import { Position } from 'vs/editor/common/core/position';
import {
ITreeElement, IExpression, IExpressionContainer, IProcess, IStackFrame, IExceptionBreakpoint, IBreakpoint, IFunctionBreakpoint, IModel,
ITreeElement, IExpression, IExpressionContainer, IProcess, IStackFrame, IExceptionBreakpoint, IBreakpoint, IFunctionBreakpoint, IModel, IReplElementSource,
IConfig, ISession, IThread, IRawModelUpdate, IScope, IRawStoppedDetails, IEnablement, IRawBreakpoint, IExceptionInfo, IReplElement, ProcessState
} from 'vs/workbench/parts/debug/common/debug';
import { Source } from 'vs/workbench/parts/debug/common/debugSource';
......@@ -30,7 +30,7 @@ const MAX_REPL_LENGTH = 10000;
export abstract class AbstractOutputElement implements IReplElement {
private static ID_COUNTER = 0;
constructor(private id = AbstractOutputElement.ID_COUNTER++) {
constructor(public sourceData: IReplElementSource, private id = AbstractOutputElement.ID_COUNTER++) {
// noop
}
......@@ -47,8 +47,9 @@ export class OutputElement extends AbstractOutputElement {
constructor(
public value: string,
public severity: severity,
source: IReplElementSource,
) {
super();
super(source);
}
public toString(): string {
......@@ -60,8 +61,8 @@ export class OutputNameValueElement extends AbstractOutputElement implements IEx
private static MAX_CHILDREN = 1000; // upper bound of children per value
constructor(public name: string, public valueObj: any, public annotation?: string) {
super();
constructor(public name: string, public valueObj: any, source?: IReplElementSource, public annotation?: string) {
super(source);
}
public get value(): string {
......@@ -379,18 +380,8 @@ export class StackFrame implements IStackFrame {
}
public openInEditor(editorService: IWorkbenchEditorService, preserveFocus?: boolean, sideBySide?: boolean): TPromise<any> {
return !this.source.available ? TPromise.as(null) : editorService.openEditor({
resource: this.source.uri,
description: this.source.origin,
options: {
preserveFocus,
selection: this.range,
revealIfVisible: true,
revealInCenterIfOutsideViewport: true,
pinned: !preserveFocus && !this.source.inMemory
}
}, sideBySide);
return !this.source.available ? TPromise.as(null) :
this.source.openInEditor(editorService, this.range, preserveFocus, sideBySide);
}
}
......@@ -960,22 +951,23 @@ export class Model implements IModel {
.then(() => this._onDidChangeREPLElements.fire());
}
public appendToRepl(output: string | IExpression, severity: severity): void {
public appendToRepl(output: string | IExpression, severity: severity, source?: IReplElementSource): void {
if (typeof output === 'string') {
const previousOutput = this.replElements.length && (this.replElements[this.replElements.length - 1] as OutputElement);
const toAdd = output.split('\n').map(line => new OutputElement(line, severity));
if (previousOutput instanceof OutputElement && severity === previousOutput.severity && toAdd.length) {
previousOutput.value += toAdd.shift().value;
}
if (previousOutput && previousOutput.value === '' && previousOutput.severity !== severity) {
const toAdd = output.split('\n').map((line, index) => new OutputElement(line, severity, index === 0 ? source : undefined));
if (previousOutput && previousOutput.value === '') {
// remove potential empty lines between different output types
this.replElements.pop();
}
if (previousOutput instanceof OutputElement && severity === previousOutput.severity && toAdd.length && toAdd[0].sourceData === previousOutput.sourceData) {
previousOutput.value += toAdd.shift().value;
}
this.addReplElements(toAdd);
} else {
// TODO@Isidor hack, we should introduce a new type which is an output that can fetch children like an expression
(<any>output).severity = severity;
(<any>output).sourceData = source;
this.addReplElements([output]);
}
......
......@@ -4,8 +4,11 @@
*--------------------------------------------------------------------------------------------*/
import * as nls from 'vs/nls';
import { TPromise } from 'vs/base/common/winjs.base';
import uri from 'vs/base/common/uri';
import { DEBUG_SCHEME } from 'vs/workbench/parts/debug/common/debug';
import { IRange } from 'vs/editor/common/core/range';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
const UNKNOWN_SOURCE_LABEL = nls.localize('unknownSource', "Unknown Source");
......@@ -46,4 +49,18 @@ export class Source {
public get inMemory() {
return this.uri.scheme === DEBUG_SCHEME;
}
public openInEditor(editorService: IWorkbenchEditorService, selection: IRange, preserveFocus?: boolean, sideBySide?: boolean): TPromise<any> {
return !this.available ? TPromise.as(null) : editorService.openEditor({
resource: this.uri,
description: this.origin,
options: {
preserveFocus,
selection,
revealIfVisible: true,
revealInCenterIfOutsideViewport: true,
pinned: !preserveFocus && !this.inMemory
}
}, sideBySide);
}
}
......@@ -199,7 +199,7 @@ export class DebugService implements debug.IDebugService {
}
// show object
this.logToRepl(new OutputNameValueElement((<any>a).prototype, a, nls.localize('snapshotObj', "Only primitive values are shown for this object.")), sev);
this.logToRepl(new OutputNameValueElement((<any>a).prototype, a, undefined, nls.localize('snapshotObj', "Only primitive values are shown for this object.")), sev);
}
// string: watch out for % replacement directive
......@@ -335,17 +335,23 @@ export class DebugService implements debug.IDebugService {
return;
}
const source = event.body.source ? {
lineNumber: event.body.line,
column: event.body.column,
source: process.getSource(event.body.source)
} : undefined;
if (event.body.variablesReference) {
const container = new ExpressionContainer(process, event.body.variablesReference, generateUuid());
container.getChildren().then(children => {
children.forEach(child => {
// Since we can not display multiple trees in a row, we are displaying these variables one after the other (ignoring their names)
child.name = null;
this.logToRepl(child, outputSeverity);
this.logToRepl(child, outputSeverity, source);
});
});
} else if (typeof event.body.output === 'string') {
this.logToRepl(event.body.output, outputSeverity);
this.logToRepl(event.body.output, outputSeverity, source);
}
}));
......@@ -594,12 +600,12 @@ export class DebugService implements debug.IDebugService {
this.model.removeReplExpressions();
}
public logToRepl(value: string | debug.IExpression, sev = severity.Info): void {
public logToRepl(value: string | debug.IExpression, sev = severity.Info, source?: debug.IReplElementSource): void {
if (typeof value === 'string' && '[2J'.localeCompare(value) === 0) {
// [2J is the ansi escape sequence for clearing the display http://ascii-table.com/ansi-escape-sequences.php
this.model.removeReplExpressions();
} else {
this.model.appendToRepl(value, sev);
this.model.appendToRepl(value, sev, source);
}
}
......
......@@ -6,6 +6,8 @@
import * as nls from 'vs/nls';
import { TPromise } from 'vs/base/common/winjs.base';
import { IAction } from 'vs/base/common/actions';
import * as lifecycle from 'vs/base/common/lifecycle';
import * as errors from 'vs/base/common/errors';
import { isFullWidthCharacter, removeAnsiEscapeCodes, endsWith } from 'vs/base/common/strings';
import { IActionItem } from 'vs/base/browser/ui/actionbar/actionbar';
import * as dom from 'vs/base/browser/dom';
......@@ -13,7 +15,7 @@ import severity from 'vs/base/common/severity';
import { IMouseEvent } from 'vs/base/browser/mouseEvent';
import { ITree, IAccessibilityProvider, ContextMenuEvent, IDataSource, IRenderer, IActionProvider } from 'vs/base/parts/tree/browser/tree';
import { ICancelableEvent } from 'vs/base/parts/tree/browser/treeDefaults';
import { IExpressionContainer, IExpression } from 'vs/workbench/parts/debug/common/debug';
import { IExpressionContainer, IExpression, IReplElementSource } from 'vs/workbench/parts/debug/common/debug';
import { Model, OutputNameValueElement, Expression, OutputElement, Variable } from 'vs/workbench/parts/debug/common/debugModel';
import { renderVariable, renderExpressionValue, IVariableTemplateData, BaseDebugController } from 'vs/workbench/parts/debug/electron-browser/debugViewer';
import { ClearReplAction } from 'vs/workbench/parts/debug/browser/debugActions';
......@@ -63,6 +65,9 @@ interface IExpressionTemplateData {
interface IValueOutputTemplateData {
container: HTMLElement;
value: HTMLElement;
source: HTMLElement;
getReplElementSource(): IReplElementSource;
toDispose: lifecycle.IDisposable[];
}
interface IKeyValueOutputTemplateData {
......@@ -174,10 +179,25 @@ export class ReplExpressionsRenderer implements IRenderer {
if (templateId === ReplExpressionsRenderer.VALUE_OUTPUT_TEMPLATE_ID) {
let data: IValueOutputTemplateData = Object.create(null);
dom.addClass(container, 'output');
let expression = dom.append(container, $('.output.expression'));
let expression = dom.append(container, $('.output.expression.value-and-source'));
data.container = container;
data.value = dom.append(expression, $('span.value'));
data.source = dom.append(expression, $('.source'));
data.toDispose = [];
data.toDispose.push(dom.addDisposableListener(data.source, 'click', e => {
e.preventDefault();
e.stopPropagation();
const source = data.getReplElementSource();
if (source) {
source.source.openInEditor(this.editorService, {
startLineNumber: source.lineNumber,
startColumn: source.column,
endLineNumber: source.lineNumber,
endColumn: source.column
}).done(undefined, errors.onUnexpectedError);
}
}));
return data;
}
......@@ -236,6 +256,9 @@ export class ReplExpressionsRenderer implements IRenderer {
}
dom.addClass(templateData.value, (output.severity === severity.Warning) ? 'warn' : (output.severity === severity.Error) ? 'error' : 'info');
templateData.source.textContent = output.sourceData ? `${output.sourceData.source.name}:${output.sourceData.lineNumber}` : '';
templateData.source.title = output.sourceData ? output.sourceData.source.uri.toString() : '';
templateData.getReplElementSource = () => output.sourceData;
}
private renderOutputNameValue(tree: ITree, output: OutputNameValueElement, templateData: IKeyValueOutputTemplateData): void {
......@@ -352,7 +375,9 @@ export class ReplExpressionsRenderer implements IRenderer {
}
public disposeTemplate(tree: ITree, templateId: string, templateData: any): void {
// noop
if (templateData.toDispose) {
lifecycle.dispose(templateData.toDispose);
}
}
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册