Skip to content

Commit 1dcb49a

Browse files
szuendDevtools-frontend LUCI CQ
authored andcommitted
[console] Move pinned expression evaluation to ConsolePin
[email protected] Bug: 407750444 Change-Id: I8b4b11cc5f9f075d45516797c7beff271edfabf5 Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/7210893 Commit-Queue: Simon Zünd <[email protected]> Reviewed-by: Philip Pfaffe <[email protected]>
1 parent 104eee3 commit 1dcb49a

File tree

1 file changed

+59
-24
lines changed

1 file changed

+59
-24
lines changed

front_end/panels/console/ConsolePinPane.ts

Lines changed: 59 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -176,10 +176,9 @@ export class ConsolePinPane extends UI.Widget.VBox {
176176

177177
export class ConsolePinPresenter {
178178
private readonly pin: ConsolePin;
179+
private readonly pinEditor: ConsolePinEditor;
179180
private readonly pinElement: Element;
180181
private readonly pinPreview: HTMLElement;
181-
private lastResult: SDK.RuntimeModel.EvaluationResult|null;
182-
private lastExecutionContext: SDK.RuntimeModel.ExecutionContext|null;
183182
private editor: TextEditor.TextEditor.TextEditor;
184183
private hovered: boolean;
185184
private lastNode: SDK.RemoteObject.RemoteObject|null;
@@ -217,13 +216,16 @@ export class ConsolePinPresenter {
217216
UI.Tooltip.Tooltip.install(nameElement, expression);
218217
elementToConsolePin.set(this.pinElement, this);
219218

220-
this.lastResult = null;
221-
this.lastExecutionContext = null;
222219
this.hovered = false;
223220
this.lastNode = null;
224221
this.editor = this.#createEditor(expression, nameElement);
225222

226-
this.pin = new ConsolePin({workingCopy: () => this.editor.state.doc.toString()}, expression);
223+
this.pinEditor = {
224+
workingCopy: () => this.editor.state.doc.toString(),
225+
workingCopyWithHint: () => TextEditor.Config.contentIncludingHint(this.editor.editor),
226+
isEditing: () => this.pinElement.hasFocus(),
227+
};
228+
this.pin = new ConsolePin(this.pinEditor, expression);
227229

228230
this.pinPreview.addEventListener('mouseenter', this.setHovered.bind(this, true), false);
229231
this.pinPreview.addEventListener('mouseleave', this.setHovered.bind(this, false), false);
@@ -356,32 +358,29 @@ export class ConsolePinPresenter {
356358
}
357359

358360
appendToContextMenu(contextMenu: UI.ContextMenu.ContextMenu): void {
359-
if (this.lastResult && !('error' in this.lastResult) && this.lastResult.object) {
360-
contextMenu.appendApplicableItems(this.lastResult.object);
361+
const {lastResult} = this.pin;
362+
if (lastResult && !('error' in lastResult) && lastResult.object) {
363+
contextMenu.appendApplicableItems(lastResult.object);
361364
// Prevent result from being released automatically, since it may be used by
362365
// the context menu action. It will be released when the console is cleared,
363366
// where we release the 'live-expression' object group.
364-
this.lastResult = null;
367+
this.pin.skipReleaseLastResult();
365368
}
366369
}
367370

368371
async updatePreview(): Promise<void> {
369372
if (!this.editor) {
370373
return;
371374
}
372-
const text = TextEditor.Config.contentIncludingHint(this.editor.editor);
373-
const isEditing = this.pinElement.hasFocus();
374-
const throwOnSideEffect = isEditing && text !== this.pin.expression;
375-
const timeout = throwOnSideEffect ? 250 : undefined;
376375
const executionContext = UI.Context.Context.instance().flavor(SDK.RuntimeModel.ExecutionContext);
377-
const {preview, result} = await ObjectUI.JavaScriptREPL.JavaScriptREPL.evaluateAndBuildPreview(
378-
text, throwOnSideEffect, true /* replMode */, timeout, !isEditing /* allowErrors */, 'live-expression',
379-
true /* awaitPromise */, true /* silent */);
380-
if (this.lastResult && this.lastExecutionContext) {
381-
this.lastExecutionContext.runtimeModel.releaseEvaluationResult(this.lastResult);
376+
if (!executionContext) {
377+
return;
382378
}
383-
this.lastResult = result || null;
384-
this.lastExecutionContext = executionContext || null;
379+
380+
const {result, node} = await this.pin.evaluate(executionContext);
381+
382+
const formatter = new ObjectUI.RemoteObjectPreviewFormatter.RemoteObjectPreviewFormatter();
383+
const preview = result ? formatter.renderEvaluationResultPreview(result) : document.createDocumentFragment();
385384

386385
const previewText = preview.deepTextContent();
387386
if (!previewText || previewText !== this.pinPreview.deepTextContent()) {
@@ -392,16 +391,12 @@ export class ConsolePinPresenter {
392391
UI.Tooltip.Tooltip.install(sideEffectLabel, i18nString(UIStrings.evaluateAllowingSideEffects));
393392
} else if (previewText) {
394393
this.pinPreview.appendChild(preview);
395-
} else if (!isEditing) {
394+
} else if (!this.pinEditor.isEditing()) {
396395
UI.UIUtils.createTextChild(this.pinPreview, i18nString(UIStrings.notAvailable));
397396
}
398397
UI.Tooltip.Tooltip.install(this.pinPreview, previewText);
399398
}
400399

401-
let node: SDK.RemoteObject.RemoteObject|null = null;
402-
if (result && !('error' in result) && result.object.type === 'object' && result.object.subtype === 'node') {
403-
node = result.object;
404-
}
405400
if (this.hovered) {
406401
if (node) {
407402
SDK.OverlayModel.OverlayModel.highlightObjectAsDOMNode(node);
@@ -422,6 +417,8 @@ export class ConsolePinPresenter {
422417
*/
423418
interface ConsolePinEditor {
424419
workingCopy(): string;
420+
workingCopyWithHint(): string;
421+
isEditing(): boolean;
425422
}
426423

427424
/**
@@ -431,6 +428,11 @@ export class ConsolePin {
431428
readonly #editor: ConsolePinEditor;
432429
#expression: string;
433430

431+
// We track the last evaluation result for this pin so we can release the RemoteObject.
432+
#lastResult: SDK.RuntimeModel.EvaluationResult|null = null;
433+
#lastExecutionContext: SDK.RuntimeModel.ExecutionContext|null = null;
434+
#releaseLastResult = true;
435+
434436
constructor(editor: ConsolePinEditor, expression: string) {
435437
this.#editor = editor;
436438
this.#expression = expression;
@@ -440,6 +442,14 @@ export class ConsolePin {
440442
return this.#expression;
441443
}
442444

445+
get lastResult(): SDK.RuntimeModel.EvaluationResult|null {
446+
return this.#lastResult;
447+
}
448+
449+
skipReleaseLastResult(): void {
450+
this.#releaseLastResult = false;
451+
}
452+
443453
/**
444454
* Commit the current working copy from the editor.
445455
* @returns true, iff the working copy was commited as-is.
@@ -450,4 +460,29 @@ export class ConsolePin {
450460
this.#expression = trimmedText;
451461
return this.#expression === text;
452462
}
463+
464+
/** Evaluates the current working copy of the pinned expression. If the result is a DOM node, we return that separately for convenience. */
465+
async evaluate(executionContext: SDK.RuntimeModel.ExecutionContext):
466+
Promise<{result: SDK.RuntimeModel.EvaluationResult | null, node: SDK.RemoteObject.RemoteObject|null}> {
467+
const editorText = this.#editor.workingCopyWithHint();
468+
const throwOnSideEffect = this.#editor.isEditing() && editorText !== this.#expression;
469+
const timeout = throwOnSideEffect ? 250 : undefined;
470+
471+
const result = await ObjectUI.JavaScriptREPL.JavaScriptREPL.evaluate(
472+
editorText, executionContext, throwOnSideEffect, /* replMode*/ true, timeout, 'live-expression',
473+
/* awaitPromise */ true, /* silent */ true);
474+
475+
if (this.#lastResult && this.#releaseLastResult) {
476+
this.#lastExecutionContext?.runtimeModel.releaseEvaluationResult(this.#lastResult);
477+
}
478+
479+
this.#lastResult = result;
480+
this.#lastExecutionContext = executionContext;
481+
this.#releaseLastResult = true;
482+
483+
if (result && !('error' in result) && result.object.type === 'object' && result.object.subtype === 'node') {
484+
return {result, node: result.object};
485+
}
486+
return {result, node: null};
487+
}
453488
}

0 commit comments

Comments
 (0)