提交 4bbd43ed 编写于 作者: I isidor

debug: Support objects when sending output to debug console

fixes #15796
上级 db40db0a
......@@ -36,18 +36,20 @@ export abstract class AbstractOutputElement implements debug.ITreeElement {
}
}
export class ValueOutputElement extends AbstractOutputElement {
export class OutputElement extends AbstractOutputElement {
public counter: number;
constructor(
public value: string,
public severity: severity,
public counter = 1
) {
super();
this.counter = 1;
}
}
export class NameValueOutputElement extends AbstractOutputElement {
export class OutputNameValueElement extends AbstractOutputElement {
private static MAX_CHILDREN = 1000; // upper bound of children per value
......@@ -71,11 +73,11 @@ export class NameValueOutputElement extends AbstractOutputElement {
public getChildren(): debug.ITreeElement[] {
if (Array.isArray(this.valueObj)) {
return (<any[]>this.valueObj).slice(0, NameValueOutputElement.MAX_CHILDREN)
.map((v, index) => new NameValueOutputElement(String(index), v));
return (<any[]>this.valueObj).slice(0, OutputNameValueElement.MAX_CHILDREN)
.map((v, index) => new OutputNameValueElement(String(index), v));
} else if (isObject(this.valueObj)) {
return Object.getOwnPropertyNames(this.valueObj).slice(0, NameValueOutputElement.MAX_CHILDREN)
.map(key => new NameValueOutputElement(key, this.valueObj[key]));
return Object.getOwnPropertyNames(this.valueObj).slice(0, OutputNameValueElement.MAX_CHILDREN)
.map(key => new OutputNameValueElement(key, this.valueObj[key]));
}
return [];
......@@ -174,7 +176,7 @@ export abstract class ExpressionContainer implements debug.IExpressionContainer
}
export class OutputExpressionContainer extends ExpressionContainer {
constructor(public name: string, stackFrame: debug.IStackFrame, reference: number, public annotation: string) {
constructor(public name: string, stackFrame: debug.IStackFrame, reference: number, public annotation = null) {
super(stackFrame, reference, generateUuid());
}
}
......@@ -818,25 +820,19 @@ export class Model implements debug.IModel {
.then(() => this._onDidChangeREPLElements.fire());
}
public appendReplOutput(value: string | { [key: string]: any }, severity?: severity): void {
const previousOutput = this.replElements.length && (this.replElements[this.replElements.length - 1] as ValueOutputElement);
if (typeof value === 'string') {
const groupTogether = previousOutput instanceof ValueOutputElement && severity === previousOutput.severity;
if (groupTogether) {
if (strings.endsWith(previousOutput.value, '\n') && previousOutput.value === value && value.trim()) {
// we got the same output (but not an empty string when trimmed) so we just increment the counter
previousOutput.counter++;
} else {
// append to previous line if same group
previousOutput.value += value;
}
public appendReplOutput(output: OutputElement | OutputExpressionContainer | OutputNameValueElement): void {
const previousOutput = this.replElements.length && (this.replElements[this.replElements.length - 1] as OutputElement);
const groupTogether = output instanceof OutputElement && previousOutput instanceof OutputElement && output.severity === previousOutput.severity;
if (groupTogether) {
if (strings.endsWith(previousOutput.value, '\n') && previousOutput.value === output.value && output.value.trim()) {
// we got the same output (but not an empty string when trimmed) so we just increment the counter
previousOutput.counter++;
} else {
this.addReplElement(new ValueOutputElement(value, severity));
// append to previous line if same group
previousOutput.value += output.value;
}
} else {
// key-value output
this.addReplElement(new NameValueOutputElement((<any>value).prototype, value, nls.localize('snapshotObj', "Only primitive values are shown for this object.")));
this.addReplElement(output);
}
this._onDidChangeREPLElements.fire();
......
......@@ -36,7 +36,7 @@ import { IEditorGroupService } from 'vs/workbench/services/group/common/groupSer
import { asFileEditorInput } from 'vs/workbench/common/editor';
import * as debug from 'vs/workbench/parts/debug/common/debug';
import { RawDebugSession } from 'vs/workbench/parts/debug/electron-browser/rawDebugSession';
import { Model, ExceptionBreakpoint, FunctionBreakpoint, Breakpoint, Expression } from 'vs/workbench/parts/debug/common/debugModel';
import { Model, ExceptionBreakpoint, FunctionBreakpoint, Breakpoint, Expression, OutputElement, OutputExpressionContainer, OutputNameValueElement } from 'vs/workbench/parts/debug/common/debugModel';
import { DebugStringEditorInput, DebugErrorEditorInput } from 'vs/workbench/parts/debug/browser/debugEditorInputs';
import { ViewModel } from 'vs/workbench/parts/debug/common/debugViewModel';
import * as debugactions from 'vs/workbench/parts/debug/browser/debugActions';
......@@ -195,12 +195,12 @@ export class DebugService implements debug.IDebugService {
// flush any existing simple values logged
if (simpleVals.length) {
this.model.appendReplOutput(simpleVals.join(' '), sev);
this.model.appendReplOutput(new OutputElement(simpleVals.join(' '), sev));
simpleVals = [];
}
// show object
this.model.appendReplOutput(a, sev);
this.model.appendReplOutput(new OutputNameValueElement((<any>a).prototype, a, nls.localize('snapshotObj', "Only primitive values are shown for this object.")));
}
// string: watch out for % replacement directive
......@@ -229,7 +229,7 @@ export class DebugService implements debug.IDebugService {
// flush simple values
if (simpleVals.length) {
this.model.appendReplOutput(simpleVals.join(' '), sev);
this.model.appendReplOutput(new OutputElement(simpleVals.join(' '), sev));
}
}
}
......@@ -325,7 +325,10 @@ export class DebugService implements debug.IDebugService {
this.customTelemetryService.publicLog(event.body.output, event.body.data);
}
} else if (event.body && typeof event.body.output === 'string' && event.body.output.length > 0) {
this.onOutput(event);
const outputSeverity = event.body.category === 'stderr' ? severity.Error : event.body.category === 'console' ? severity.Warning : severity.Info;
const value = event.body.variablesReference ? new OutputExpressionContainer(event.body.output, this.viewModel.focusedStackFrame, event.body.variablesReference)
: new OutputElement(event.body.output, outputSeverity);
this.model.appendReplOutput(value);
}
}));
......@@ -366,11 +369,6 @@ export class DebugService implements debug.IDebugService {
});
}
private onOutput(event: DebugProtocol.OutputEvent): void {
const outputSeverity = event.body.category === 'stderr' ? severity.Error : event.body.category === 'console' ? severity.Warning : severity.Info;
this.model.appendReplOutput(event.body.output, outputSeverity);
}
private loadBreakpoints(): Breakpoint[] {
let result: Breakpoint[];
try {
......
......@@ -20,7 +20,7 @@ import { IActionProvider } from 'vs/base/parts/tree/browser/actionsRenderer';
import { ICancelableEvent } from 'vs/base/parts/tree/browser/treeDefaults';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { IExpressionContainer, IExpression, IDebugService } from 'vs/workbench/parts/debug/common/debug';
import { Model, NameValueOutputElement, Expression, ValueOutputElement, Variable, OutputExpressionContainer } from 'vs/workbench/parts/debug/common/debugModel';
import { Model, OutputNameValueElement, Expression, OutputElement, Variable, OutputExpressionContainer } from 'vs/workbench/parts/debug/common/debugModel';
import { renderVariable, renderExpressionValue, IVariableTemplateData, BaseDebugController } from 'vs/workbench/parts/debug/electron-browser/debugViewer';
import { AddToWatchExpressionsAction, ClearReplAction } from 'vs/workbench/parts/debug/browser/debugActions';
import { CopyAction } from 'vs/workbench/parts/debug/electron-browser/electronDebugActions';
......@@ -38,17 +38,17 @@ export class ReplExpressionsDataSource implements IDataSource {
}
public hasChildren(tree: ITree, element: any): boolean {
return element instanceof Model || (<IExpressionContainer>element).hasChildren || element instanceof NameValueOutputElement;
return element instanceof Model || (<IExpressionContainer>element).hasChildren || element instanceof OutputNameValueElement;
}
public getChildren(tree: ITree, element: any): TPromise<any> {
if (element instanceof Model) {
return TPromise.as(element.getReplElements());
}
if (element instanceof NameValueOutputElement) {
if (element instanceof OutputNameValueElement) {
return TPromise.as(element.getChildren());
}
if (element instanceof ValueOutputElement) {
if (element instanceof OutputElement) {
return TPromise.as(null);
}
......@@ -143,10 +143,10 @@ export class ReplExpressionsRenderer implements IRenderer {
if (element instanceof Expression) {
return ReplExpressionsRenderer.EXPRESSION_TEMPLATE_ID;
}
if (element instanceof ValueOutputElement) {
if (element instanceof OutputElement) {
return ReplExpressionsRenderer.VALUE_OUTPUT_TEMPLATE_ID;
}
if (element instanceof NameValueOutputElement || element instanceof OutputExpressionContainer) {
if (element instanceof OutputNameValueElement || element instanceof OutputExpressionContainer) {
return ReplExpressionsRenderer.NAME_VALUE_OUTPUT_TEMPLATE_ID;
}
......@@ -224,7 +224,7 @@ export class ReplExpressionsRenderer implements IRenderer {
}
}
private renderOutputValue(output: ValueOutputElement, templateData: IValueOutputTemplateData): void {
private renderOutputValue(output: OutputElement, templateData: IValueOutputTemplateData): void {
// counter
if (output.counter > 1) {
......@@ -397,7 +397,7 @@ export class ReplExpressionsRenderer implements IRenderer {
}, event.ctrlKey || event.metaKey).done(null, errors.onUnexpectedError);
}
private renderOutputNameValue(tree: ITree, output: NameValueOutputElement | OutputExpressionContainer, templateData: IKeyValueOutputTemplateData): void {
private renderOutputNameValue(tree: ITree, output: OutputNameValueElement | OutputExpressionContainer, templateData: IKeyValueOutputTemplateData): void {
// key
if (output.name) {
......@@ -436,11 +436,11 @@ export class ReplExpressionsAccessibilityProvider implements IAccessibilityProvi
if (element instanceof Expression) {
return nls.localize('replExpressionAriaLabel', "Expression {0} has value {1}, read eval print loop, debug", (<Expression>element).name, (<Expression>element).value);
}
if (element instanceof ValueOutputElement) {
return nls.localize('replValueOutputAriaLabel', "{0}, read eval print loop, debug", (<ValueOutputElement>element).value);
if (element instanceof OutputElement) {
return nls.localize('replValueOutputAriaLabel', "{0}, read eval print loop, debug", (<OutputElement>element).value);
}
if (element instanceof NameValueOutputElement) {
return nls.localize('replKeyValueOutputAriaLabel', "Output variable {0} has value {1}, read eval print loop, debug", (<NameValueOutputElement>element).name, (<NameValueOutputElement>element).value);
if (element instanceof OutputNameValueElement) {
return nls.localize('replKeyValueOutputAriaLabel', "Output variable {0} has value {1}, read eval print loop, debug", (<OutputNameValueElement>element).name, (<OutputNameValueElement>element).value);
}
return null;
......@@ -499,7 +499,7 @@ export class ReplExpressionsController extends BaseDebugController {
protected onLeftClick(tree: ITree, element: any, eventish: ICancelableEvent, origin: string = 'mouse'): boolean {
const mouseEvent = <IMouseEvent>eventish;
// input and output are one element in the tree => we only expand if the user clicked on the output.
if ((element.reference > 0 || (element instanceof NameValueOutputElement && element.getChildren().length > 0)) && mouseEvent.target.className.indexOf('input expression') === -1) {
if ((element.reference > 0 || (element instanceof OutputNameValueElement && element.getChildren().length > 0)) && mouseEvent.target.className.indexOf('input expression') === -1) {
super.onLeftClick(tree, element, eventish, origin);
tree.clearFocus();
tree.deselect(element);
......
......@@ -6,16 +6,16 @@
import assert = require('assert');
import uri from 'vs/base/common/uri';
import severity from 'vs/base/common/severity';
import debugmodel = require('vs/workbench/parts/debug/common/debugModel');
import { OutputElement, Model, Process, Expression, OutputNameValueElement, StackFrame, Thread } from 'vs/workbench/parts/debug/common/debugModel';
import * as sinon from 'sinon';
import { MockSession } from 'vs/workbench/parts/debug/test/common/mockDebug';
suite('Debug - Model', () => {
let model: debugmodel.Model;
let model: Model;
let rawSession: MockSession;
setup(() => {
model = new debugmodel.Model([], true, [], [], []);
model = new Model([], true, [], [], []);
rawSession = new MockSession();
});
......@@ -278,7 +278,7 @@ suite('Debug - Model', () => {
// Expressions
function assertWatchExpressions(watchExpressions: debugmodel.Expression[], expectedName: string) {
function assertWatchExpressions(watchExpressions: Expression[], expectedName: string) {
assert.equal(watchExpressions.length, 2);
watchExpressions.forEach(we => {
assert.equal(we.available, false);
......@@ -289,9 +289,9 @@ suite('Debug - Model', () => {
test('watch expressions', () => {
assert.equal(model.getWatchExpressions().length, 0);
const process = new debugmodel.Process('mockProcess', rawSession);
const thread = new debugmodel.Thread(process, 'mockthread', 1);
const stackFrame = new debugmodel.StackFrame(thread, 1, null, 'app.js', 1, 1);
const process = new Process('mockProcess', rawSession);
const thread = new Thread(process, 'mockthread', 1);
const stackFrame = new StackFrame(thread, 1, null, 'app.js', 1, 1);
model.addWatchExpression(process, stackFrame, 'console').done();
model.addWatchExpression(process, stackFrame, 'console').done();
const watchExpressions = model.getWatchExpressions();
......@@ -310,18 +310,18 @@ suite('Debug - Model', () => {
test('repl expressions', () => {
assert.equal(model.getReplElements().length, 0);
const process = new debugmodel.Process('mockProcess', rawSession);
const thread = new debugmodel.Thread(process, 'mockthread', 1);
const stackFrame = new debugmodel.StackFrame(thread, 1, null, 'app.js', 1, 1);
const process = new Process('mockProcess', rawSession);
const thread = new Thread(process, 'mockthread', 1);
const stackFrame = new StackFrame(thread, 1, null, 'app.js', 1, 1);
model.addReplExpression(process, stackFrame, 'myVariable').done();
model.addReplExpression(process, stackFrame, 'myVariable').done();
model.addReplExpression(process, stackFrame, 'myVariable').done();
assert.equal(model.getReplElements().length, 3);
model.getReplElements().forEach(re => {
assert.equal((<debugmodel.Expression>re).available, false);
assert.equal((<debugmodel.Expression>re).name, 'myVariable');
assert.equal((<debugmodel.Expression>re).reference, 0);
assert.equal((<Expression>re).available, false);
assert.equal((<Expression>re).name, 'myVariable');
assert.equal((<Expression>re).reference, 0);
});
model.removeReplExpressions();
......@@ -331,12 +331,12 @@ suite('Debug - Model', () => {
// Repl output
test('repl output', () => {
model.appendReplOutput('first line\n', severity.Error);
model.appendReplOutput('second line\n', severity.Warning);
model.appendReplOutput('second line\n', severity.Warning);
model.appendReplOutput('second line\n', severity.Error);
model.appendReplOutput(new OutputElement('first line\n', severity.Error));
model.appendReplOutput(new OutputElement('second line\n', severity.Warning));
model.appendReplOutput(new OutputElement('second line\n', severity.Warning));
model.appendReplOutput(new OutputElement('second line\n', severity.Error));
let elements = <debugmodel.ValueOutputElement[]>model.getReplElements();
let elements = <OutputElement[]>model.getReplElements();
assert.equal(elements.length, 3);
assert.equal(elements[0].value, 'first line\n');
assert.equal(elements[0].counter, 1);
......@@ -345,30 +345,30 @@ suite('Debug - Model', () => {
assert.equal(elements[1].counter, 2);
assert.equal(elements[1].severity, severity.Warning);
model.appendReplOutput('1', severity.Warning);
model.appendReplOutput('2', severity.Warning);
model.appendReplOutput('3', severity.Warning);
elements = <debugmodel.ValueOutputElement[]>model.getReplElements();
model.appendReplOutput(new OutputElement('1', severity.Warning));
model.appendReplOutput(new OutputElement('2', severity.Warning));
model.appendReplOutput(new OutputElement('3', severity.Warning));
elements = <OutputElement[]>model.getReplElements();
assert.equal(elements.length, 4);
assert.equal(elements[3].value, '123');
assert.equal(elements[3].severity, severity.Warning);
const keyValueObject = { 'key1': 2, 'key2': 'value' };
model.appendReplOutput(keyValueObject, severity.Info);
const element = <debugmodel.NameValueOutputElement>model.getReplElements()[4];
model.appendReplOutput(new OutputNameValueElement('fake', keyValueObject));
const element = <OutputNameValueElement>model.getReplElements()[4];
assert.equal(element.value, 'Object');
assert.deepEqual(element.valueObj, keyValueObject);
const multiLineContent = 'multi line \n string \n last line';
model.appendReplOutput(multiLineContent, severity.Info);
const multiLineElement = <debugmodel.ValueOutputElement>model.getReplElements()[5];
model.appendReplOutput(new OutputElement(multiLineContent, severity.Info));
const multiLineElement = <OutputElement>model.getReplElements()[5];
assert.equal(multiLineElement.value, multiLineContent);
assert.equal(model.getReplElements().length, 6);
model.appendReplOutput('second line', severity.Warning);
model.appendReplOutput('second line', severity.Warning);
model.appendReplOutput(new OutputElement('second line', severity.Warning));
model.appendReplOutput(new OutputElement('second line', severity.Warning));
assert.equal((<debugmodel.ValueOutputElement>model.getReplElements()[6]).value, 'second linesecond line');
assert.equal((<OutputElement>model.getReplElements()[6]).value, 'second linesecond line');
model.removeReplExpressions();
assert.equal(model.getReplElements().length, 0);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册