diff --git a/src/vs/workbench/contrib/debug/browser/baseDebugView.ts b/src/vs/workbench/contrib/debug/browser/baseDebugView.ts index 67f2b1ade60ac26537bd06fff95d2b1b04a54c26..f9dee4d8ca9561aa71d7668dfbbf8a75c744ebff 100644 --- a/src/vs/workbench/contrib/debug/browser/baseDebugView.ts +++ b/src/vs/workbench/contrib/debug/browser/baseDebugView.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as dom from 'vs/base/browser/dom'; -import { IExpression, IDebugService } from 'vs/workbench/contrib/debug/common/debug'; +import { IExpression, IDebugService, IExpressionContainer } from 'vs/workbench/contrib/debug/common/debug'; import { Expression, Variable, ExpressionContainer } from 'vs/workbench/contrib/debug/common/debugModel'; import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { IInputValidationOptions, InputBox } from 'vs/base/browser/ui/inputbox/inputBox'; @@ -50,7 +50,7 @@ export function replaceWhitespace(value: string): string { return value.replace(/[\n\r\t]/g, char => map[char]); } -export function renderExpressionValue(expressionOrValue: IExpression | string, container: HTMLElement, options: IRenderValueOptions): void { +export function renderExpressionValue(expressionOrValue: IExpressionContainer | string, container: HTMLElement, options: IRenderValueOptions): void { let value = typeof expressionOrValue === 'string' ? expressionOrValue : expressionOrValue.value; // remove stale classes @@ -221,4 +221,4 @@ export abstract class AbstractExpressionsRenderer implements ITreeRenderer { - await this.repl.addReplExpression(stackFrame, name); + const expressionEvaluated = this.repl.addReplExpression(stackFrame, name); + this._onDidChangeREPLElements.fire(); + await expressionEvaluated; this._onDidChangeREPLElements.fire(); // Evaluate all watch expressions and fetch variables again since repl evaluation might have changed some. variableSetEmitter.fire(); diff --git a/src/vs/workbench/contrib/debug/browser/media/repl.css b/src/vs/workbench/contrib/debug/browser/media/repl.css index 7268724a2812137683531979071b02b2bb838c93..b0ab0e6e71b58db3903b8b4f7dd6637fb5451506 100644 --- a/src/vs/workbench/contrib/debug/browser/media/repl.css +++ b/src/vs/workbench/contrib/debug/browser/media/repl.css @@ -48,11 +48,13 @@ cursor: text; } -.repl .repl-tree .output.expression > .value { +.repl .repl-tree .output.expression > .value, +.repl .repl-tree .evaluation-result.expression > .value { margin-left: 0px; } -.repl .repl-tree .output.expression > .annotation { +.repl .repl-tree .output.expression > .annotation, +.repl .repl-tree .evaluation-result.expression > .annotation { font-size: inherit; padding-left: 6px; } @@ -67,7 +69,7 @@ } /* Only show 'stale expansion' info when the element gets expanded. */ -.repl .repl-tree .input-output-pair > .output > .annotation::before { +.repl .repl-tree .evaluation-result > .annotation::before { content: ''; } @@ -134,7 +136,8 @@ .monaco-workbench .repl .repl-tree .output.expression .code-underline { text-decoration: underline; } /* Links */ -.monaco-workbench .repl .repl-tree .output.expression a { +.monaco-workbench .repl .repl-tree .output.expression a, +.monaco-workbench .repl .repl-tree .evaluation-result.expression a { text-decoration: underline; cursor: pointer; } diff --git a/src/vs/workbench/contrib/debug/browser/repl.ts b/src/vs/workbench/contrib/debug/browser/repl.ts index 4412e5bc72039e2af016d645fb477e5d16edc32a..f00dc8c6d999489ce4740d114f02c5e91e96a0e5 100644 --- a/src/vs/workbench/contrib/debug/browser/repl.ts +++ b/src/vs/workbench/contrib/debug/browser/repl.ts @@ -44,7 +44,8 @@ import { CompletionContext, CompletionList, CompletionProviderRegistry } from 'v import { first } from 'vs/base/common/arrays'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { IAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; -import { Variable, Expression, SimpleReplElement, RawObjectReplElement } from 'vs/workbench/contrib/debug/common/debugModel'; +import { Variable } from 'vs/workbench/contrib/debug/common/debugModel'; +import { SimpleReplElement, RawObjectReplElement, ReplEvaluationInput, ReplEvaluationResult } from 'vs/workbench/contrib/debug/common/replModel'; import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; import { ITreeRenderer, ITreeNode, ITreeContextMenuEvent, IAsyncDataSource } from 'vs/base/browser/ui/tree/tree'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; @@ -412,7 +413,8 @@ export class Repl extends Panel implements IPrivateReplService, IHistoryNavigati [ this.instantiationService.createInstance(VariablesRenderer), this.instantiationService.createInstance(ReplSimpleElementsRenderer), - new ReplExpressionsRenderer(), + new ReplEvaluationInputsRenderer(), + new ReplEvaluationResultsRenderer(), new ReplRawObjectsRenderer() ], // https://github.com/microsoft/TypeScript/issues/32526 @@ -568,12 +570,13 @@ export class Repl extends Panel implements IPrivateReplService, IHistoryNavigati // Repl tree -interface IExpressionTemplateData { - input: HTMLElement; - output: HTMLElement; +interface IReplEvaluationInputTemplateData { + label: HighlightedLabel; +} + +interface IReplEvaluationResultTemplateData { value: HTMLElement; annotation: HTMLElement; - label: HighlightedLabel; } interface ISimpleReplElementTemplateData { @@ -593,27 +596,46 @@ interface IRawObjectReplTemplateData { label: HighlightedLabel; } -class ReplExpressionsRenderer implements ITreeRenderer { - static readonly ID = 'expressionRepl'; +class ReplEvaluationInputsRenderer implements ITreeRenderer { + static readonly ID = 'replEvaluationInput'; get templateId(): string { - return ReplExpressionsRenderer.ID; + return ReplEvaluationInputsRenderer.ID; } - renderTemplate(container: HTMLElement): IExpressionTemplateData { - dom.addClass(container, 'input-output-pair'); - const input = dom.append(container, $('.input.expression')); + renderTemplate(container: HTMLElement): IReplEvaluationInputTemplateData { + const input = dom.append(container, $('.expression')); const label = new HighlightedLabel(input, false); - const output = dom.append(container, $('.output.expression')); + return { label }; + } + + renderElement(element: ITreeNode, index: number, templateData: IReplEvaluationInputTemplateData): void { + const evaluation = element.element; + templateData.label.set(evaluation.value, createMatches(element.filterData)); + } + + disposeTemplate(templateData: IReplEvaluationInputTemplateData): void { + // noop + } +} + +class ReplEvaluationResultsRenderer implements ITreeRenderer { + static readonly ID = 'replEvaluationResult'; + + get templateId(): string { + return ReplEvaluationResultsRenderer.ID; + } + + renderTemplate(container: HTMLElement): IReplEvaluationResultTemplateData { + const output = dom.append(container, $('.evaluation-result.expression')); const value = dom.append(output, $('span.value')); const annotation = dom.append(output, $('span')); - return { input, label, output, value, annotation }; + return { value, annotation }; } - renderElement(element: ITreeNode, index: number, templateData: IExpressionTemplateData): void { + renderElement(element: ITreeNode, index: number, templateData: IReplEvaluationResultTemplateData): void { const expression = element.element; - templateData.label.set(expression.name, createMatches(element.filterData)); renderExpressionValue(expression, templateData.value, { preserveWhitespace: !expression.hasChildren, showHover: false, @@ -625,7 +647,7 @@ class ReplExpressionsRenderer implements ITreeRenderer { const rowHeight = Math.ceil(1.4 * fontSize); const wordWrap = config.console.wordWrap; if (!wordWrap) { - return element instanceof Expression ? 2 * rowHeight : rowHeight; + return rowHeight; } // In order to keep scroll position we need to give a good approximation to the tree // For every 150 characters increase the number of lines needed - if (element instanceof Expression) { - let { name, value } = element; - let nameRows = countNumberOfLines(name) + Math.floor(name.length / 150); + if (element instanceof ReplEvaluationResult) { + let value = element.value; if (element.hasChildren) { - return (nameRows + 1) * rowHeight; + return rowHeight; } let valueRows = value ? (countNumberOfLines(value) + Math.floor(value.length / 150)) : 0; - return rowHeight * (nameRows + valueRows); + return rowHeight * valueRows; } - if (element instanceof SimpleReplElement) { + if (element instanceof SimpleReplElement || element instanceof ReplEvaluationInput) { let value = element.value; let valueRows = countNumberOfLines(value) + Math.floor(value.length / 150); @@ -788,8 +809,11 @@ class ReplDelegate implements IListVirtualDelegate { if (element instanceof Variable && element.name) { return VariablesRenderer.ID; } - if (element instanceof Expression) { - return ReplExpressionsRenderer.ID; + if (element instanceof ReplEvaluationResult) { + return ReplEvaluationResultsRenderer.ID; + } + if (element instanceof ReplEvaluationInput) { + return ReplEvaluationInputsRenderer.ID; } if (element instanceof SimpleReplElement || (element instanceof Variable && !element.name)) { // Variable with no name is a top level variable which should be rendered like a repl element #17404 @@ -836,10 +860,7 @@ class ReplAccessibilityProvider implements IAccessibilityProvider if (element instanceof Variable) { return nls.localize('replVariableAriaLabel', "Variable {0} has value {1}, read eval print loop, debug", element.name, element.value); } - if (element instanceof Expression) { - return nls.localize('replExpressionAriaLabel', "Expression {0} has value {1}, read eval print loop, debug", element.name, element.value); - } - if (element instanceof SimpleReplElement) { + if (element instanceof SimpleReplElement || element instanceof ReplEvaluationInput || element instanceof ReplEvaluationResult) { return nls.localize('replValueOutputAriaLabel', "{0}, read eval print loop, debug", element.value); } if (element instanceof RawObjectReplElement) { diff --git a/src/vs/workbench/contrib/debug/common/debug.ts b/src/vs/workbench/contrib/debug/common/debug.ts index 69cb45f57a3eac1dd7ee992bc97f2d75adb1eb32..f146ee3ca151ac3fd86d7d3ea86173fe0d985b12 100644 --- a/src/vs/workbench/contrib/debug/common/debug.ts +++ b/src/vs/workbench/contrib/debug/common/debug.ts @@ -104,13 +104,13 @@ export interface IExpressionContainer extends ITreeElement { readonly hasChildren: boolean; getChildren(): Promise; readonly reference?: number; + readonly value: string; + readonly type?: string; + valueChanged?: boolean; } -export interface IExpression extends IReplElement, IExpressionContainer { +export interface IExpression extends IExpressionContainer { name: string; - readonly value: string; - valueChanged?: boolean; - readonly type?: string; } export interface IDebugger { diff --git a/src/vs/workbench/contrib/debug/common/debugModel.ts b/src/vs/workbench/contrib/debug/common/debugModel.ts index b33ffe111afd88e57972d1871c60bef16856d19c..ae2347115de49739ee7636cccc8b8c1cbef63748 100644 --- a/src/vs/workbench/contrib/debug/common/debugModel.ts +++ b/src/vs/workbench/contrib/debug/common/debugModel.ts @@ -10,13 +10,12 @@ import * as lifecycle from 'vs/base/common/lifecycle'; import { Event, Emitter } from 'vs/base/common/event'; import { generateUuid } from 'vs/base/common/uuid'; import { RunOnceScheduler } from 'vs/base/common/async'; -import severity from 'vs/base/common/severity'; -import { isObject, isString, isUndefinedOrNull } from 'vs/base/common/types'; +import { isString, isUndefinedOrNull } from 'vs/base/common/types'; import { distinct, lastIndex } from 'vs/base/common/arrays'; import { Range, IRange } from 'vs/editor/common/core/range'; import { - ITreeElement, IExpression, IExpressionContainer, IDebugSession, IStackFrame, IExceptionBreakpoint, IBreakpoint, IFunctionBreakpoint, IDebugModel, IReplElementSource, - IThread, IRawModelUpdate, IScope, IRawStoppedDetails, IEnablement, IBreakpointData, IExceptionInfo, IReplElement, IBreakpointsChangeEvent, IBreakpointUpdateData, IBaseBreakpoint, State, IDataBreakpoint + ITreeElement, IExpression, IExpressionContainer, IDebugSession, IStackFrame, IExceptionBreakpoint, IBreakpoint, IFunctionBreakpoint, IDebugModel, + IThread, IRawModelUpdate, IScope, IRawStoppedDetails, IEnablement, IBreakpointData, IExceptionInfo, IBreakpointsChangeEvent, IBreakpointUpdateData, IBaseBreakpoint, State, IDataBreakpoint } from 'vs/workbench/contrib/debug/common/debug'; import { Source, UNKNOWN_SOURCE_LABEL } from 'vs/workbench/contrib/debug/common/debugSource'; import { commonSuffixLength } from 'vs/base/common/strings'; @@ -25,75 +24,13 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { ITextEditor } from 'vs/workbench/common/editor'; -export class SimpleReplElement implements IReplElement { - constructor( - private id: string, - public value: string, - public severity: severity, - public sourceData?: IReplElementSource, - ) { } - - toString(): string { - return this.value; - } - - getId(): string { - return this.id; - } -} - -export class RawObjectReplElement implements IExpression { - - private static readonly MAX_CHILDREN = 1000; // upper bound of children per value - - constructor(private id: string, public name: string, public valueObj: any, public sourceData?: IReplElementSource, public annotation?: string) { } - - getId(): string { - return this.id; - } - - get value(): string { - if (this.valueObj === null) { - return 'null'; - } else if (Array.isArray(this.valueObj)) { - return `Array[${this.valueObj.length}]`; - } else if (isObject(this.valueObj)) { - return 'Object'; - } else if (isString(this.valueObj)) { - return `"${this.valueObj}"`; - } - - return String(this.valueObj) || ''; - } - - get hasChildren(): boolean { - return (Array.isArray(this.valueObj) && this.valueObj.length > 0) || (isObject(this.valueObj) && Object.getOwnPropertyNames(this.valueObj).length > 0); - } - - getChildren(): Promise { - let result: IExpression[] = []; - if (Array.isArray(this.valueObj)) { - result = (this.valueObj).slice(0, RawObjectReplElement.MAX_CHILDREN) - .map((v, index) => new RawObjectReplElement(`${this.id}:${index}`, String(index), v)); - } else if (isObject(this.valueObj)) { - result = Object.getOwnPropertyNames(this.valueObj).slice(0, RawObjectReplElement.MAX_CHILDREN) - .map((key, index) => new RawObjectReplElement(`${this.id}:${index}`, key, this.valueObj[key])); - } - - return Promise.resolve(result); - } - - toString(): string { - return `${this.name}\n${this.value}`; - } -} - export class ExpressionContainer implements IExpressionContainer { public static allValues = new Map(); // Use chunks to support variable paging #9537 private static readonly BASE_CHUNK_SIZE = 100; + public type: string | undefined; public valueChanged = false; private _value: string = ''; protected children?: Promise; @@ -195,50 +132,57 @@ export class ExpressionContainer implements IExpressionContainer { toString(): string { return this.value; } -} -export class Expression extends ExpressionContainer implements IExpression { - static DEFAULT_VALUE = nls.localize('notAvailable', "not available"); - - public available: boolean; - public type: string | undefined; - - constructor(public name: string, id = generateUuid()) { - super(undefined, 0, id); - this.available = false; - // name is not set if the expression is just being added - // in that case do not set default value to prevent flashing #14499 - if (name) { - this.value = Expression.DEFAULT_VALUE; - } - } + async evaluateExpression( + expression: string, + session: IDebugSession | undefined, + stackFrame: IStackFrame | undefined, + context: string): Promise { - async evaluate(session: IDebugSession | undefined, stackFrame: IStackFrame | undefined, context: string): Promise { if (!session || (!stackFrame && context !== 'repl')) { this.value = context === 'repl' ? nls.localize('startDebugFirst', "Please start a debug session to evaluate expressions") : Expression.DEFAULT_VALUE; - this.available = false; this.reference = 0; - - return Promise.resolve(undefined); + return false; } this.session = session; try { - const response = await session.evaluate(this.name, stackFrame ? stackFrame.frameId : undefined, context); - this.available = !!(response && response.body); + const response = await session.evaluate(expression, stackFrame ? stackFrame.frameId : undefined, context); if (response && response.body) { this.value = response.body.result || ''; this.reference = response.body.variablesReference; this.namedVariables = response.body.namedVariables; this.indexedVariables = response.body.indexedVariables; this.type = response.body.type || this.type; + return true; } + return false; } catch (e) { this.value = e.message || ''; - this.available = false; this.reference = 0; + return false; } } +} + +export class Expression extends ExpressionContainer implements IExpression { + static DEFAULT_VALUE = nls.localize('notAvailable', "not available"); + + public available: boolean; + + constructor(public name: string, id = generateUuid()) { + super(undefined, 0, id); + this.available = false; + // name is not set if the expression is just being added + // in that case do not set default value to prevent flashing #14499 + if (name) { + this.value = Expression.DEFAULT_VALUE; + } + } + + async evaluate(session: IDebugSession | undefined, stackFrame: IStackFrame | undefined, context: string): Promise { + this.available = await this.evaluateExpression(this.name, session, stackFrame, context); + } toString(): string { return `${this.name}\n${this.value}`; diff --git a/src/vs/workbench/contrib/debug/common/replModel.ts b/src/vs/workbench/contrib/debug/common/replModel.ts index dc981f59b67dd372e2c63cdda86afd14d7ae0656..4d60ecea55a51255322e1a0ce2dc1911eda67944 100644 --- a/src/vs/workbench/contrib/debug/common/replModel.ts +++ b/src/vs/workbench/contrib/debug/common/replModel.ts @@ -6,15 +6,105 @@ import * as nls from 'vs/nls'; import severity from 'vs/base/common/severity'; import { IReplElement, IStackFrame, IExpression, IReplElementSource, IDebugSession } from 'vs/workbench/contrib/debug/common/debug'; -import { Expression, SimpleReplElement, RawObjectReplElement } from 'vs/workbench/contrib/debug/common/debugModel'; -import { isUndefinedOrNull, isObject } from 'vs/base/common/types'; +import { ExpressionContainer } from 'vs/workbench/contrib/debug/common/debugModel'; +import { isString, isUndefinedOrNull, isObject } from 'vs/base/common/types'; import { basenameOrAuthority } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { endsWith } from 'vs/base/common/strings'; +import { generateUuid } from 'vs/base/common/uuid'; const MAX_REPL_LENGTH = 10000; let topReplElementCounter = 0; +export class SimpleReplElement implements IReplElement { + constructor( + private id: string, + public value: string, + public severity: severity, + public sourceData?: IReplElementSource, + ) { } + + toString(): string { + return this.value; + } + + getId(): string { + return this.id; + } +} + +export class RawObjectReplElement implements IExpression { + + private static readonly MAX_CHILDREN = 1000; // upper bound of children per value + + constructor(private id: string, public name: string, public valueObj: any, public sourceData?: IReplElementSource, public annotation?: string) { } + + getId(): string { + return this.id; + } + + get value(): string { + if (this.valueObj === null) { + return 'null'; + } else if (Array.isArray(this.valueObj)) { + return `Array[${this.valueObj.length}]`; + } else if (isObject(this.valueObj)) { + return 'Object'; + } else if (isString(this.valueObj)) { + return `"${this.valueObj}"`; + } + + return String(this.valueObj) || ''; + } + + get hasChildren(): boolean { + return (Array.isArray(this.valueObj) && this.valueObj.length > 0) || (isObject(this.valueObj) && Object.getOwnPropertyNames(this.valueObj).length > 0); + } + + getChildren(): Promise { + let result: IExpression[] = []; + if (Array.isArray(this.valueObj)) { + result = (this.valueObj).slice(0, RawObjectReplElement.MAX_CHILDREN) + .map((v, index) => new RawObjectReplElement(`${this.id}:${index}`, String(index), v)); + } else if (isObject(this.valueObj)) { + result = Object.getOwnPropertyNames(this.valueObj).slice(0, RawObjectReplElement.MAX_CHILDREN) + .map((key, index) => new RawObjectReplElement(`${this.id}:${index}`, key, this.valueObj[key])); + } + + return Promise.resolve(result); + } + + toString(): string { + return `${this.name}\n${this.value}`; + } +} + +export class ReplEvaluationInput implements IReplElement { + private id: string; + + constructor(public value: string) { + this.id = generateUuid(); + } + + toString(): string { + return this.value; + } + + getId(): string { + return this.id; + } +} + +export class ReplEvaluationResult extends ExpressionContainer implements IReplElement { + constructor() { + super(undefined, 0, generateUuid()); + } + + toString(): string { + return `${this.value}`; + } +} + export class ReplModel { private replElements: IReplElement[] = []; @@ -24,10 +114,11 @@ export class ReplModel { return this.replElements; } - addReplExpression(stackFrame: IStackFrame | undefined, name: string): Promise { - const expression = new Expression(name); - this.addReplElement(expression); - return expression.evaluate(this.session, stackFrame, 'repl'); + async addReplExpression(stackFrame: IStackFrame | undefined, name: string): Promise { + this.addReplElement(new ReplEvaluationInput(name)); + const result = new ReplEvaluationResult(); + await result.evaluateExpression(name, this.session, stackFrame, 'repl'); + this.addReplElement(result); } appendToRepl(data: string | IExpression, sev: severity, source?: IReplElementSource): void { diff --git a/src/vs/workbench/contrib/debug/test/browser/debugModel.test.ts b/src/vs/workbench/contrib/debug/test/browser/debugModel.test.ts index f93108d3341c356c069dac56a09442ad207961f5..e69860a2fac93e1910f2e1ab2ffdf706ed917ccf 100644 --- a/src/vs/workbench/contrib/debug/test/browser/debugModel.test.ts +++ b/src/vs/workbench/contrib/debug/test/browser/debugModel.test.ts @@ -6,12 +6,12 @@ import * as assert from 'assert'; import { URI as uri } from 'vs/base/common/uri'; import severity from 'vs/base/common/severity'; -import { SimpleReplElement, DebugModel, Expression, RawObjectReplElement, StackFrame, Thread } from 'vs/workbench/contrib/debug/common/debugModel'; +import { DebugModel, Expression, StackFrame, Thread } from 'vs/workbench/contrib/debug/common/debugModel'; import * as sinon from 'sinon'; import { MockRawSession } from 'vs/workbench/contrib/debug/test/common/mockDebug'; import { Source } from 'vs/workbench/contrib/debug/common/debugSource'; import { DebugSession } from 'vs/workbench/contrib/debug/browser/debugSession'; -import { ReplModel } from 'vs/workbench/contrib/debug/common/replModel'; +import { SimpleReplElement, RawObjectReplElement, ReplEvaluationInput, ReplModel } from 'vs/workbench/contrib/debug/common/replModel'; import { IBreakpointUpdateData } from 'vs/workbench/contrib/debug/common/debug'; import { NullOpenerService } from 'vs/platform/opener/common/opener'; @@ -348,9 +348,7 @@ suite('Debug - Model', () => { assert.equal(replModel.getReplElements().length, 3); replModel.getReplElements().forEach(re => { - assert.equal((re).available, false); - assert.equal((re).name, 'myVariable'); - assert.equal((re).reference, 0); + assert.equal((re).value, 'myVariable'); }); replModel.removeReplExpressions(); diff --git a/test/smoke/src/areas/debug/debugSmoke.ts b/test/smoke/src/areas/debug/debugSmoke.ts index 4132ec5a9ccb2dc18a5cfaeb8c61b9d5fefcf896..25b170107036345e38c1cd27d0558479a89f25fa 100644 --- a/test/smoke/src/areas/debug/debugSmoke.ts +++ b/test/smoke/src/areas/debug/debugSmoke.ts @@ -28,7 +28,7 @@ const STACK_FRAME = `${VIEWLET} .monaco-list-row .stack-frame`; const SPECIFIC_STACK_FRAME = filename => `${STACK_FRAME} .file[title*="${filename}"]`; const VARIABLE = `${VIEWLET} .debug-variables .monaco-list-row .expression`; const CONSOLE_OUTPUT = `.repl .output.expression .value`; -const CONSOLE_INPUT_OUTPUT = `.repl .input-output-pair .output.expression .value`; +const CONSOLE_EVALUATION_RESULT = `.repl .evaluation-result.expression .value`; const REPL_FOCUSED = '.repl-input-wrapper .monaco-editor textarea'; @@ -132,8 +132,8 @@ export class Debug extends Viewlet { // Wait for the keys to be picked up by the editor model such that repl evalutes what just got typed await this.editor.waitForEditorContents('debug:replinput', s => s.indexOf(text) >= 0); await this.code.dispatchKeybinding('enter'); - await this.code.waitForElement(CONSOLE_INPUT_OUTPUT); - await this.waitForOutput(output => accept(output[output.length - 1] || '')); + await this.code.waitForElements(CONSOLE_EVALUATION_RESULT, false, + elements => !!elements.length && accept(elements[elements.length - 1].textContent)); } // Different node versions give different number of variables. As a workaround be more relaxed when checking for variable count