66import type { Terminal , IDisposable , ITerminalAddon , IDecoration } from '@xterm/xterm' ;
77import type { SearchAddon as ISearchApi , ISearchOptions , ISearchDecorationOptions } from '@xterm/addon-search' ;
88import { Emitter , Event } from 'vs/base/common/event' ;
9- import { combinedDisposable , Disposable , dispose , MutableDisposable , toDisposable } from 'vs/base/common/lifecycle' ;
9+ import { Disposable , dispose , MutableDisposable , toDisposable } from 'vs/base/common/lifecycle' ;
10+ import { SearchLineCache } from './SearchLineCache' ;
1011
1112interface IInternalSearchOptions {
1213 noScroll ?: boolean ;
@@ -33,16 +34,7 @@ export interface ISearchResult {
3334 size : number ;
3435}
3536
36- type LineCacheEntry = [
37- /**
38- * The string representation of a line (as opposed to the buffer cell representation).
39- */
40- lineAsString : string ,
41- /**
42- * The offsets where each line starts when the entry describes a wrapped line.
43- */
44- lineOffsets : number [ ]
45- ] ;
37+
4638
4739interface IHighlight extends IDisposable {
4840 decoration : IDecoration ;
@@ -65,11 +57,7 @@ const enum Constants {
6557 */
6658 NON_WORD_CHARACTERS = ' ~!@#$%^&*()+`-=[]{}|\\;:"\',./<>?' ,
6759
68- /**
69- * Time-to-live for cached search results in milliseconds. After this duration, cached search
70- * results will be invalidated to ensure they remain consistent with terminal content changes.
71- */
72- LINES_CACHE_TIME_TO_LIVE = 15000 ,
60+
7361
7462 /**
7563 * Default maximum number of search results to highlight simultaneously. This limit prevents
@@ -89,14 +77,7 @@ export class SearchAddon extends Disposable implements ITerminalAddon, ISearchAp
8977 private _highlightLimit : number ;
9078 private _lastSearchOptions : ISearchOptions | undefined ;
9179 private _highlightTimeout : number | undefined ;
92- /**
93- * translateBufferLineToStringWithWrap is a fairly expensive call.
94- * We memoize the calls into an array that has a time based ttl.
95- * _linesCache is also invalidated when the terminal cursor moves.
96- */
97- private _linesCache : LineCacheEntry [ ] | undefined ;
98- private _linesCacheTimeoutId = 0 ;
99- private _linesCacheDisposables = new MutableDisposable ( ) ;
80+ private _lineCache = this . _register ( new MutableDisposable < SearchLineCache > ( ) ) ;
10081
10182 private readonly _onDidChangeResults = this . _register ( new Emitter < ISearchResultChangeEvent > ( ) ) ;
10283 public get onDidChangeResults ( ) : Event < ISearchResultChangeEvent > { return this . _onDidChangeResults . event ; }
@@ -109,6 +90,7 @@ export class SearchAddon extends Disposable implements ITerminalAddon, ISearchAp
10990
11091 public activate ( terminal : Terminal ) : void {
11192 this . _terminal = terminal ;
93+ this . _lineCache . value = new SearchLineCache ( terminal ) ;
11294 this . _register ( this . _terminal . onWriteParsed ( ( ) => this . _updateMatches ( ) ) ) ;
11395 this . _register ( this . _terminal . onResize ( ( ) => this . _updateMatches ( ) ) ) ;
11496 this . _register ( toDisposable ( ( ) => this . clearDecorations ( ) ) ) ;
@@ -223,7 +205,7 @@ export class SearchAddon extends Disposable implements ITerminalAddon, ISearchAp
223205
224206 let result : ISearchResult | undefined = undefined ;
225207
226- this . _initLinesCache ( ) ;
208+ this . _lineCache . value ! . initLinesCache ( ) ;
227209
228210 const searchPosition : ISearchPosition = {
229211 startRow,
@@ -271,7 +253,7 @@ export class SearchAddon extends Disposable implements ITerminalAddon, ISearchAp
271253 }
272254 }
273255
274- this . _initLinesCache ( ) ;
256+ this . _lineCache . value ! . initLinesCache ( ) ;
275257
276258 const searchPosition : ISearchPosition = {
277259 startRow,
@@ -392,7 +374,7 @@ export class SearchAddon extends Disposable implements ITerminalAddon, ISearchAp
392374 let startCol = this . _terminal . cols ;
393375 const isReverseSearch = true ;
394376
395- this . _initLinesCache ( ) ;
377+ this . _lineCache . value ! . initLinesCache ( ) ;
396378 const searchPosition : ISearchPosition = {
397379 startRow,
398380 startCol
@@ -443,32 +425,7 @@ export class SearchAddon extends Disposable implements ITerminalAddon, ISearchAp
443425 return this . _selectResult ( result , searchOptions ?. decorations , internalSearchOptions ?. noScroll ) ;
444426 }
445427
446- /**
447- * Sets up a line cache with a ttl
448- */
449- private _initLinesCache ( ) : void {
450- const terminal = this . _terminal ! ;
451- if ( ! this . _linesCache ) {
452- this . _linesCache = new Array ( terminal . buffer . active . length ) ;
453- this . _linesCacheDisposables . value = combinedDisposable (
454- terminal . onLineFeed ( ( ) => this . _destroyLinesCache ( ) ) ,
455- terminal . onCursorMove ( ( ) => this . _destroyLinesCache ( ) ) ,
456- terminal . onResize ( ( ) => this . _destroyLinesCache ( ) )
457- ) ;
458- }
459428
460- window . clearTimeout ( this . _linesCacheTimeoutId ) ;
461- this . _linesCacheTimeoutId = window . setTimeout ( ( ) => this . _destroyLinesCache ( ) , Constants . LINES_CACHE_TIME_TO_LIVE ) ;
462- }
463-
464- private _destroyLinesCache ( ) : void {
465- this . _linesCache = undefined ;
466- this . _linesCacheDisposables . clear ( ) ;
467- if ( this . _linesCacheTimeoutId ) {
468- window . clearTimeout ( this . _linesCacheTimeoutId ) ;
469- this . _linesCacheTimeoutId = 0 ;
470- }
471- }
472429
473430 /**
474431 * A found substring is a whole word if it doesn't have an alphanumeric character directly
@@ -513,12 +470,10 @@ export class SearchAddon extends Disposable implements ITerminalAddon, ISearchAp
513470 searchPosition . startCol += terminal . cols ;
514471 return this . _findInLine ( term , searchPosition , searchOptions ) ;
515472 }
516- let cache = this . _linesCache ?. [ row ] ;
473+ let cache = this . _lineCache . value ! . getLineFromCache ( row ) ;
517474 if ( ! cache ) {
518- cache = this . _translateBufferLineToStringWithWrap ( row , true ) ;
519- if ( this . _linesCache ) {
520- this . _linesCache [ row ] = cache ;
521- }
475+ cache = this . _lineCache . value ! . translateBufferLineToStringWithWrap ( row , true ) ;
476+ this . _lineCache . value ! . setLineInCache ( row , cache ) ;
522477 }
523478 const [ stringLine , offsets ] = cache ;
524479
@@ -639,42 +594,7 @@ export class SearchAddon extends Disposable implements ITerminalAddon, ISearchAp
639594 return offset ;
640595 }
641596
642- /**
643- * Translates a buffer line to a string, including subsequent lines if they are wraps.
644- * Wide characters will count as two columns in the resulting string. This
645- * function is useful for getting the actual text underneath the raw selection
646- * position.
647- * @param lineIndex The index of the line being translated.
648- * @param trimRight Whether to trim whitespace to the right.
649- */
650- private _translateBufferLineToStringWithWrap ( lineIndex : number , trimRight : boolean ) : LineCacheEntry {
651- const terminal = this . _terminal ! ;
652- const strings = [ ] ;
653- const lineOffsets = [ 0 ] ;
654- let line = terminal . buffer . active . getLine ( lineIndex ) ;
655- while ( line ) {
656- const nextLine = terminal . buffer . active . getLine ( lineIndex + 1 ) ;
657- const lineWrapsToNext = nextLine ? nextLine . isWrapped : false ;
658- let string = line . translateToString ( ! lineWrapsToNext && trimRight ) ;
659- if ( lineWrapsToNext && nextLine ) {
660- const lastCell = line . getCell ( line . length - 1 ) ;
661- const lastCellIsNull = lastCell && lastCell . getCode ( ) === 0 && lastCell . getWidth ( ) === 1 ;
662- // a wide character wrapped to the next line
663- if ( lastCellIsNull && nextLine . getCell ( 0 ) ?. getWidth ( ) === 2 ) {
664- string = string . slice ( 0 , - 1 ) ;
665- }
666- }
667- strings . push ( string ) ;
668- if ( lineWrapsToNext ) {
669- lineOffsets . push ( lineOffsets [ lineOffsets . length - 1 ] + string . length ) ;
670- } else {
671- break ;
672- }
673- lineIndex ++ ;
674- line = nextLine ;
675- }
676- return [ strings . join ( '' ) , lineOffsets ] ;
677- }
597+
678598
679599 /**
680600 * Selects and scrolls to a result.
0 commit comments