@@ -8,7 +8,7 @@ import { renderIcon } from '../../../../../../base/browser/ui/iconLabel/iconLabe
88import { Codicon } from '../../../../../../base/common/codicons.js' ;
99import { Emitter , Event } from '../../../../../../base/common/event.js' ;
1010import { Disposable , DisposableStore , MutableDisposable , toDisposable } from '../../../../../../base/common/lifecycle.js' ;
11- import { IObservable , autorun , autorunWithStore , constObservable , derived , observableSignalFromEvent , observableValue } from '../../../../../../base/common/observable.js' ;
11+ import { IObservable , autorun , autorunWithStore , constObservable , derived , derivedOpts , observableSignalFromEvent , observableValue } from '../../../../../../base/common/observable.js' ;
1212import * as strings from '../../../../../../base/common/strings.js' ;
1313import { applyFontInfo } from '../../../../../browser/config/domFontInfo.js' ;
1414import { ContentWidgetPositionPreference , ICodeEditor , IContentWidgetPosition , IViewZoneChangeAccessor , MouseTargetType } from '../../../../../browser/editorBrowser.js' ;
@@ -34,7 +34,8 @@ import { CodeEditorWidget } from '../../../../../browser/widget/codeEditor/codeE
3434import { TokenWithTextArray } from '../../../../../common/tokens/tokenWithTextArray.js' ;
3535import { InlineCompletionViewData } from '../inlineEdits/inlineEditsViewInterface.js' ;
3636import { InlineDecorationType } from '../../../../../common/viewModel/inlineDecorations.js' ;
37- import { sum } from '../../../../../../base/common/arrays.js' ;
37+ import { equals , sum } from '../../../../../../base/common/arrays.js' ;
38+ import { equalsIfDefined , IEquatable , itemEquals } from '../../../../../../base/common/equals.js' ;
3839
3940export interface IGhostTextWidgetData {
4041 readonly ghostText : GhostText | GhostTextReplacement ;
@@ -102,14 +103,14 @@ export class GhostTextView extends Disposable {
102103 this . _additionalLinesWidget = this . _register (
103104 new AdditionalLinesWidget (
104105 this . _editor ,
105- derived ( reader => {
106+ derivedOpts ( { owner : this , equalsFn : equalsIfDefined ( itemEquals ( ) ) } , reader => {
106107 /** @description lines */
107108 const uiState = this . _state . read ( reader ) ;
108- return uiState ? {
109- lineNumber : uiState . lineNumber ,
110- additionalLines : uiState . additionalLines ,
111- minReservedLineCount : uiState . additionalReservedLineCount ,
112- } : undefined ;
109+ return uiState ? new AdditionalLinesData (
110+ uiState . lineNumber ,
111+ uiState . additionalLines ,
112+ uiState . additionalReservedLineCount ,
113+ ) : undefined ;
113114 } ) ,
114115 this . _shouldKeepCursorStable ,
115116 this . _isClickable
@@ -243,10 +244,10 @@ export class GhostTextView extends Disposable {
243244 const existingContent = t . slice ( additionalLinesOriginalSuffix . columnRange . toZeroBasedOffsetRange ( ) ) ;
244245 content = TokenWithTextArray . fromLineTokens ( content ) . append ( existingContent ) . toLineTokens ( content . languageIdCodec ) ;
245246 }
246- return {
247+ return new LineData (
247248 content ,
248- decorations : l . decorations ,
249- } ;
249+ l . decorations ,
250+ ) ;
250251 } ) ;
251252
252253 const cursorColumn = this . _editor . getSelection ( ) ?. getStartPosition ( ) . column ! ;
@@ -420,6 +421,24 @@ function computeGhostTextViewData(ghostText: GhostText | GhostTextReplacement, t
420421 } ;
421422}
422423
424+ class AdditionalLinesData implements IEquatable < AdditionalLinesData > {
425+ constructor (
426+ public readonly lineNumber : number ,
427+ public readonly additionalLines : readonly LineData [ ] ,
428+ public readonly minReservedLineCount : number ,
429+ ) { }
430+
431+ equals ( other : AdditionalLinesData ) : boolean {
432+ if ( this . lineNumber !== other . lineNumber ) {
433+ return false ;
434+ }
435+ if ( this . minReservedLineCount !== other . minReservedLineCount ) {
436+ return false ;
437+ }
438+ return equals ( this . additionalLines , other . additionalLines , itemEquals ( ) ) ;
439+ }
440+ }
441+
423442export class AdditionalLinesWidget extends Disposable {
424443 private _viewZoneInfo : { viewZoneId : string ; heightInLines : number ; lineNumber : number } | undefined ;
425444 public get viewZoneId ( ) : string | undefined { return this . _viewZoneInfo ?. viewZoneId ; }
@@ -440,11 +459,7 @@ export class AdditionalLinesWidget extends Disposable {
440459
441460 constructor (
442461 private readonly _editor : ICodeEditor ,
443- private readonly _lines : IObservable < {
444- lineNumber : number ;
445- additionalLines : LineData [ ] ;
446- minReservedLineCount : number ;
447- } | undefined > ,
462+ private readonly _lines : IObservable < AdditionalLinesData | undefined > ,
448463 private readonly _shouldKeepCursorStable : boolean ,
449464 private readonly _isClickable : boolean ,
450465 ) {
@@ -500,7 +515,7 @@ export class AdditionalLinesWidget extends Disposable {
500515 } ) ;
501516 }
502517
503- private updateLines ( lineNumber : number , additionalLines : LineData [ ] , minReservedLineCount : number ) : void {
518+ private updateLines ( lineNumber : number , additionalLines : readonly LineData [ ] , minReservedLineCount : number ) : void {
504519 const textModel = this . _editor . getModel ( ) ;
505520 if ( ! textModel ) {
506521 return ;
@@ -581,12 +596,21 @@ function isTargetGhostText(target: EventTarget | null): boolean {
581596 return isHTMLElement ( target ) && target . classList . contains ( GHOST_TEXT_CLASS_NAME ) ;
582597}
583598
584- export interface LineData {
585- content : LineTokens ; // Must not contain a linebreak!
586- decorations : LineDecoration [ ] ;
599+ export class LineData implements IEquatable < LineData > {
600+ constructor (
601+ public readonly content : LineTokens , // Must not contain a linebreak!
602+ public readonly decorations : readonly LineDecoration [ ]
603+ ) { }
604+
605+ equals ( other : LineData ) : boolean {
606+ if ( ! this . content . equals ( other . content ) ) {
607+ return false ;
608+ }
609+ return LineDecoration . equalsArr ( this . decorations , other . decorations ) ;
610+ }
587611}
588612
589- function renderLines ( domNode : HTMLElement , tabSize : number , lines : LineData [ ] , opts : IComputedEditorOptions , isClickable : boolean ) : void {
613+ function renderLines ( domNode : HTMLElement , tabSize : number , lines : readonly LineData [ ] , opts : IComputedEditorOptions , isClickable : boolean ) : void {
590614 const disableMonospaceOptimizations = opts . get ( EditorOption . disableMonospaceOptimizations ) ;
591615 const stopRenderingLineAfter = opts . get ( EditorOption . stopRenderingLineAfter ) ;
592616 // To avoid visual confusion, we don't want to render visible whitespace
@@ -625,7 +649,7 @@ function renderLines(domNode: HTMLElement, tabSize: number, lines: LineData[], o
625649 containsRTL ,
626650 0 ,
627651 lineTokens ,
628- lineData . decorations ,
652+ lineData . decorations . slice ( ) ,
629653 tabSize ,
630654 0 ,
631655 fontInfo . spaceWidth ,
0 commit comments