From 9c118b9eb3be2c19cd68cb776005a9f02df1402f Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 22 Jul 2016 10:25:48 +0200 Subject: [PATCH] debug: support variable paging #9537 --- .../parts/debug/common/debugModel.ts | 70 +++++++++++-------- .../debug/electron-browser/debugService.ts | 3 +- .../parts/debug/test/node/debugModel.test.ts | 6 +- 3 files changed, 46 insertions(+), 33 deletions(-) diff --git a/src/vs/workbench/parts/debug/common/debugModel.ts b/src/vs/workbench/parts/debug/common/debugModel.ts index 607900c2bff..2e1dd471566 100644 --- a/src/vs/workbench/parts/debug/common/debugModel.ts +++ b/src/vs/workbench/parts/debug/common/debugModel.ts @@ -18,20 +18,6 @@ import { Source } from 'vs/workbench/parts/debug/common/debugSource'; const MAX_REPL_LENGTH = 10000; const UNKNOWN_SOURCE_LABEL = nls.localize('unknownSource', "Unknown Source"); -function resolveChildren(debugService: debug.IDebugService, parent: debug.IExpressionContainer): TPromise { - 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 { 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: if (response.body) { expression.value = response.body.result; expression.reference = response.body.variablesReference; + expression.childrenCount = response.body.totalCount; } return expression; @@ -229,21 +216,46 @@ export class KeyValueOutputElement extends OutputElement { export abstract class ExpressionContainer implements debug.IExpressionContainer { - private children: TPromise; - public valueChanged: boolean; public static allValues: { [id: string]: string } = {}; + // Use chunks to support variable paging #9537 + private static CHUNK_SIZE = 100; + + public valueChanged: boolean; + private children: TPromise; private _value: string; - constructor(public reference: number, private id: string, private cacheChildren: boolean) { - this.children = null; + constructor(public reference: number, private id: string, private cacheChildren: boolean, public childrenCount: number, private chunkIndex = 0) { + // noop } public getChildren(debugService: debug.IDebugService): TPromise { - if (!this.cacheChildren) { - return resolveChildren(debugService, this); - } - if (!this.children) { - this.children = resolveChildren(debugService, this); + if (!this.cacheChildren || !this.children) { + const session = debugService.getActiveSession(); + // only variables with reference > 0 have children. + if (!session || this.reference <= 0) { + 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; @@ -271,7 +283,7 @@ export class Expression extends ExpressionContainer implements debug.IExpression public available: boolean; constructor(public name: string, cacheChildren: boolean, id = uuid.generateUuid()) { - super(0, id, cacheChildren); + super(0, id, cacheChildren, 0); this.value = Expression.DEFAULT_VALUE; this.available = false; } @@ -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 public errorMessage: string; - constructor(public parent: debug.IExpressionContainer, reference: number, public name: string, value: string, public type: string = null, public available = true) { - super(reference, `variable:${ parent.getId() }:${ name }`, 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, childrenCount, chunkIndex); this.value = massageValue(value); } } export class Scope extends ExpressionContainer implements debug.IScope { - constructor(private threadId: number, public name: string, reference: number, public expensive: boolean) { - super(reference, `scope:${threadId}:${name}:${reference}`, true); + constructor(private threadId: number, public name: string, reference: number, public expensive: boolean, childrenCount: number) { + super(reference, `scope:${threadId}:${name}:${reference}`, true, childrenCount); } } @@ -310,7 +322,7 @@ export class StackFrame implements debug.IStackFrame { public getScopes(debugService: debug.IDebugService): TPromise { if (!this.scopes) { 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 => []); } diff --git a/src/vs/workbench/parts/debug/electron-browser/debugService.ts b/src/vs/workbench/parts/debug/electron-browser/debugService.ts index 77d4c41feef..7b0b16b2fcf 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debugService.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debugService.ts @@ -623,7 +623,8 @@ export class DebugService implements debug.IDebugService { pathFormat: 'path', linesStartAt1: true, columnsStartAt1: true, - supportsVariableType: true // #8858 + supportsVariableType: true, // #8858 + supportsVariablePaging: false // #9537 }).then((result: DebugProtocol.InitializeResponse) => { if (!this.session) { return TPromise.wrapError(new Error(nls.localize('debugAdapterCrash', "Debug adapter process has terminated unexpectedly"))); diff --git a/src/vs/workbench/parts/debug/test/node/debugModel.test.ts b/src/vs/workbench/parts/debug/test/node/debugModel.test.ts index 619cc154b76..1f7c0a6e640 100644 --- a/src/vs/workbench/parts/debug/test/node/debugModel.test.ts +++ b/src/vs/workbench/parts/debug/test/node/debugModel.test.ts @@ -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('son', false), type), 'son'); - const scope = new debugmodel.Scope(1, 'myscope', 1, false); - const son = new debugmodel.Variable(new debugmodel.Variable(new debugmodel.Variable(scope, 0, 'grandfather', '75'), 0, 'father', '45'), 0, 'son', '20'); + 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', 1), 0, 'father', '45', 1), 0, 'son', '20', 1); 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\']'); }); }); -- GitLab