提交 9c118b9e 编写于 作者: I isidor

debug: support variable paging

#9537
上级 ba70479e
...@@ -18,20 +18,6 @@ import { Source } from 'vs/workbench/parts/debug/common/debugSource'; ...@@ -18,20 +18,6 @@ import { Source } from 'vs/workbench/parts/debug/common/debugSource';
const MAX_REPL_LENGTH = 10000; const MAX_REPL_LENGTH = 10000;
const UNKNOWN_SOURCE_LABEL = nls.localize('unknownSource', "Unknown Source"); const UNKNOWN_SOURCE_LABEL = nls.localize('unknownSource', "Unknown Source");
function resolveChildren(debugService: debug.IDebugService, parent: debug.IExpressionContainer): TPromise<Variable[]> {
const session = debugService.getActiveSession();
// only variables with reference > 0 have children.
if (!session || parent.reference <= 0) {
return TPromise.as([]);
}
return session.variables({ variablesReference: parent.reference }).then(response => {
return arrays.distinct(response.body.variables.filter(v => !!v), v => v.name).map(
v => new Variable(parent, v.variablesReference, v.name, v.value, v.type)
);
}, (e: Error) => [new Variable(parent, 0, null, e.message, null, false)]);
}
function massageValue(value: string): string { function massageValue(value: string): string {
return value ? value.replace(/\n/g, '\\n').replace(/\r/g, '\\r').replace(/\t/g, '\\t') : value; return value ? value.replace(/\n/g, '\\n').replace(/\r/g, '\\r').replace(/\t/g, '\\t') : value;
} }
...@@ -53,6 +39,7 @@ export function evaluateExpression(session: debug.IRawDebugSession, stackFrame: ...@@ -53,6 +39,7 @@ export function evaluateExpression(session: debug.IRawDebugSession, stackFrame:
if (response.body) { if (response.body) {
expression.value = response.body.result; expression.value = response.body.result;
expression.reference = response.body.variablesReference; expression.reference = response.body.variablesReference;
expression.childrenCount = response.body.totalCount;
} }
return expression; return expression;
...@@ -229,21 +216,46 @@ export class KeyValueOutputElement extends OutputElement { ...@@ -229,21 +216,46 @@ export class KeyValueOutputElement extends OutputElement {
export abstract class ExpressionContainer implements debug.IExpressionContainer { export abstract class ExpressionContainer implements debug.IExpressionContainer {
private children: TPromise<debug.IExpression[]>;
public valueChanged: boolean;
public static allValues: { [id: string]: string } = {}; public static allValues: { [id: string]: string } = {};
// Use chunks to support variable paging #9537
private static CHUNK_SIZE = 100;
public valueChanged: boolean;
private children: TPromise<debug.IExpression[]>;
private _value: string; private _value: string;
constructor(public reference: number, private id: string, private cacheChildren: boolean) { constructor(public reference: number, private id: string, private cacheChildren: boolean, public childrenCount: number, private chunkIndex = 0) {
this.children = null; // noop
} }
public getChildren(debugService: debug.IDebugService): TPromise<debug.IExpression[]> { public getChildren(debugService: debug.IDebugService): TPromise<debug.IExpression[]> {
if (!this.cacheChildren) { if (!this.cacheChildren || !this.children) {
return resolveChildren(debugService, this); const session = debugService.getActiveSession();
} // only variables with reference > 0 have children.
if (!this.children) { if (!session || this.reference <= 0) {
this.children = resolveChildren(debugService, this); this.children = TPromise.as([]);
} else {
if (this.childrenCount > ExpressionContainer.CHUNK_SIZE) {
// There are a lot of children, create fake intermediate values that represent chunks #9537
const chunks = [];
const numberOfChunks = this.childrenCount / ExpressionContainer.CHUNK_SIZE;
for (let i = 0; i < numberOfChunks; i++) {
const chunkName = `${i * ExpressionContainer.CHUNK_SIZE}..${(i + 1) * ExpressionContainer.CHUNK_SIZE - 1}`;
chunks.push(new Variable(this, this.reference, chunkName, '', ExpressionContainer.CHUNK_SIZE, null, true, i));
}
this.children = TPromise.as(chunks);
} else {
this.children = session.variables({
variablesReference: this.reference,
start: this.chunkIndex * ExpressionContainer.CHUNK_SIZE,
count: ExpressionContainer.CHUNK_SIZE
}).then(response => {
return arrays.distinct(response.body.variables.filter(v => !!v), v => v.name).map(
v => new Variable(this, v.variablesReference, v.name, v.value, v.totalCount, v.type)
);
}, (e: Error) => [new Variable(this, 0, null, e.message, 0, null, false)]);
}
}
} }
return this.children; return this.children;
...@@ -271,7 +283,7 @@ export class Expression extends ExpressionContainer implements debug.IExpression ...@@ -271,7 +283,7 @@ export class Expression extends ExpressionContainer implements debug.IExpression
public available: boolean; public available: boolean;
constructor(public name: string, cacheChildren: boolean, id = uuid.generateUuid()) { constructor(public name: string, cacheChildren: boolean, id = uuid.generateUuid()) {
super(0, id, cacheChildren); super(0, id, cacheChildren, 0);
this.value = Expression.DEFAULT_VALUE; this.value = Expression.DEFAULT_VALUE;
this.available = false; this.available = false;
} }
...@@ -282,16 +294,16 @@ export class Variable extends ExpressionContainer implements debug.IExpression { ...@@ -282,16 +294,16 @@ export class Variable extends ExpressionContainer implements debug.IExpression {
// Used to show the error message coming from the adapter when setting the value #7807 // Used to show the error message coming from the adapter when setting the value #7807
public errorMessage: string; public errorMessage: string;
constructor(public parent: debug.IExpressionContainer, reference: number, public name: string, value: string, public type: string = null, public available = true) { constructor(public parent: debug.IExpressionContainer, reference: number, public name: string, value: string, childrenCount: number, public type: string = null, public available = true, chunkIndex = 0) {
super(reference, `variable:${ parent.getId() }:${ name }`, true); super(reference, `variable:${ parent.getId() }:${ name }`, true, childrenCount, chunkIndex);
this.value = massageValue(value); this.value = massageValue(value);
} }
} }
export class Scope extends ExpressionContainer implements debug.IScope { export class Scope extends ExpressionContainer implements debug.IScope {
constructor(private threadId: number, public name: string, reference: number, public expensive: boolean) { constructor(private threadId: number, public name: string, reference: number, public expensive: boolean, childrenCount: number) {
super(reference, `scope:${threadId}:${name}:${reference}`, true); super(reference, `scope:${threadId}:${name}:${reference}`, true, childrenCount);
} }
} }
...@@ -310,7 +322,7 @@ export class StackFrame implements debug.IStackFrame { ...@@ -310,7 +322,7 @@ export class StackFrame implements debug.IStackFrame {
public getScopes(debugService: debug.IDebugService): TPromise<debug.IScope[]> { public getScopes(debugService: debug.IDebugService): TPromise<debug.IScope[]> {
if (!this.scopes) { if (!this.scopes) {
this.scopes = debugService.getActiveSession().scopes({ frameId: this.frameId }).then(response => { this.scopes = debugService.getActiveSession().scopes({ frameId: this.frameId }).then(response => {
return response.body.scopes.map(rs => new Scope(this.threadId, rs.name, rs.variablesReference, rs.expensive)); return response.body.scopes.map(rs => new Scope(this.threadId, rs.name, rs.variablesReference, rs.expensive, rs.totalCount));
}, err => []); }, err => []);
} }
......
...@@ -623,7 +623,8 @@ export class DebugService implements debug.IDebugService { ...@@ -623,7 +623,8 @@ export class DebugService implements debug.IDebugService {
pathFormat: 'path', pathFormat: 'path',
linesStartAt1: true, linesStartAt1: true,
columnsStartAt1: true, columnsStartAt1: true,
supportsVariableType: true // #8858 supportsVariableType: true, // #8858
supportsVariablePaging: false // #9537
}).then((result: DebugProtocol.InitializeResponse) => { }).then((result: DebugProtocol.InitializeResponse) => {
if (!this.session) { if (!this.session) {
return TPromise.wrapError(new Error(nls.localize('debugAdapterCrash', "Debug adapter process has terminated unexpectedly"))); return TPromise.wrapError(new Error(nls.localize('debugAdapterCrash', "Debug adapter process has terminated unexpectedly")));
......
...@@ -352,11 +352,11 @@ suite('Debug - Model', () => { ...@@ -352,11 +352,11 @@ suite('Debug - Model', () => {
assert.equal(debugmodel.getFullExpressionName(new debugmodel.Expression(null, false), type), null); assert.equal(debugmodel.getFullExpressionName(new debugmodel.Expression(null, false), type), null);
assert.equal(debugmodel.getFullExpressionName(new debugmodel.Expression('son', false), type), 'son'); assert.equal(debugmodel.getFullExpressionName(new debugmodel.Expression('son', false), type), 'son');
const scope = new debugmodel.Scope(1, 'myscope', 1, false); const scope = new debugmodel.Scope(1, 'myscope', 1, false, 1);
const son = new debugmodel.Variable(new debugmodel.Variable(new debugmodel.Variable(scope, 0, 'grandfather', '75'), 0, 'father', '45'), 0, 'son', '20'); const son = new debugmodel.Variable(new debugmodel.Variable(new debugmodel.Variable(scope, 0, 'grandfather', '75', 1), 0, 'father', '45', 1), 0, 'son', '20', 1);
assert.equal(debugmodel.getFullExpressionName(son, type), 'grandfather.father.son'); assert.equal(debugmodel.getFullExpressionName(son, type), 'grandfather.father.son');
const grandson = new debugmodel.Variable(son, 0, '/weird_name', '1'); const grandson = new debugmodel.Variable(son, 0, '/weird_name', '1', 0);
assert.equal(debugmodel.getFullExpressionName(grandson, type), 'grandfather.father.son[\'/weird_name\']'); assert.equal(debugmodel.getFullExpressionName(grandson, type), 'grandfather.father.son[\'/weird_name\']');
}); });
}); });
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册