@@ -176,10 +176,9 @@ export class ConsolePinPane extends UI.Widget.VBox {
176176
177177export 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 */
423418interface 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