@@ -47,13 +47,13 @@ interface IHighlight extends IDisposable {
4747 decoration : IDecoration ;
4848 match : ISearchResult ;
4949}
50- // just a wrapper around boolean so we can keep a reference to value
50+ // just a wrapper around boolean so we can keep a reference to boolean value
5151// to make it clear: the goal is to pass a boolean by reference not value
52- type CancelSearchSignal = {
52+ interface ICancelSearchSignal {
5353 value : boolean ;
54- } ;
54+ }
5555
56- type ChunckSearchDirection = 'up' | 'down' ;
56+ type ChunkSearchDirection = 'up' | 'down' ;
5757
5858const NON_WORD_CHARACTERS = ' ~!@#$%^&*()+`-=[]{}|\\;:"\',./<>?' ;
5959const DEFAULT_HIGHLIGHT_LIMIT = 1000 ;
@@ -70,18 +70,18 @@ export class SearchAddon extends Disposable implements ITerminalAddon , ISearchA
7070 private _searchOptions : ISearchOptions | undefined ;
7171 private _debounceTimeout : number | undefined ;
7272 private _searchCompleted : boolean = true ;
73- private _cancelSearchSignal : CancelSearchSignal = { value :false } ;
73+ private _cancelSearchSignal : ICancelSearchSignal = { value :false } ;
7474 /**
75- * Number of matches in each chunck
75+ * Number of matches in each chunk
7676 */
77- private _chunckSize : number = 200 ;
77+ private _chunkSize : number = 200 ;
7878 /**
7979 * Time in ms
8080 * 1 ms seems to work fine as we just need to let other parts of the code to take over
81- * and return here when other work is done
81+ * and return here when their work is done
8282 */
83- private _timeBetweenChunckOperations = 1 ;
84- private _chunckSearchDirection : ChunckSearchDirection = 'down' ;
83+ private _timeBetweenChunkOperations = 1 ;
84+
8585 /**
8686 * This should be high enough so not to trigger a lot of searches
8787 * and subsequently a lot of canceled searches which clean up their own
@@ -97,7 +97,7 @@ export class SearchAddon extends Disposable implements ITerminalAddon , ISearchA
9797 * We memoize the calls into an array that has a time based ttl.
9898 * _linesCache is also invalidated when the terminal cursor moves.
9999 */
100- private _linesCache : LineCacheEntry [ ] | undefined ;
100+ private _linesCache : LineCacheEntry [ ] = [ ] ;
101101
102102 private readonly _onDidChangeResults = this . _register ( new Emitter < { resultIndex : number , resultCount : number , searchCompleted : boolean } > ( ) ) ;
103103 public readonly onDidChangeResults = this . _onDidChangeResults . event ;
@@ -135,7 +135,7 @@ export class SearchAddon extends Disposable implements ITerminalAddon , ISearchA
135135 }
136136
137137 /**
138- * The array needs to be in descending Marker ID order.
138+ * The array needs to be in descending Marker ID order.
139139 *
140140 * that way we get the smallest ID fist using pop
141141 *
@@ -145,16 +145,16 @@ export class SearchAddon extends Disposable implements ITerminalAddon , ISearchA
145145 */
146146 private _iterateToDisposeDecoration ( matchesWithHighlightApplied : IHighlight [ ] ) : void {
147147 setTimeout ( ( ) => {
148- this . _chunckDisposeDecoration ( matchesWithHighlightApplied ) ;
148+ this . _chunkDisposeDecoration ( matchesWithHighlightApplied ) ;
149149
150150 if ( matchesWithHighlightApplied . length > 0 ) {
151151 this . _iterateToDisposeDecoration ( matchesWithHighlightApplied ) ;
152152 }
153- } , this . _timeBetweenChunckOperations ) ;
153+ } , this . _timeBetweenChunkOperations ) ;
154154 }
155- private _chunckDisposeDecoration ( matchesWithHighlightApplied : IHighlight [ ] ) : void {
155+ private _chunkDisposeDecoration ( matchesWithHighlightApplied : IHighlight [ ] ) : void {
156156
157- const numberOfElementsToDispose = this . _chunckSize > matchesWithHighlightApplied . length ? matchesWithHighlightApplied . length : this . _chunckSize ;
157+ const numberOfElementsToDispose = this . _chunkSize > matchesWithHighlightApplied . length ? matchesWithHighlightApplied . length : this . _chunkSize ;
158158
159159 for ( let i = 0 ; i < numberOfElementsToDispose ; i ++ ) {
160160 matchesWithHighlightApplied . pop ( ) ?. dispose ( ) ;
@@ -206,13 +206,12 @@ export class SearchAddon extends Disposable implements ITerminalAddon , ISearchA
206206 window . clearTimeout ( this . _debounceTimeout ) ;
207207
208208 this . _debounceTimeout = setTimeout ( ( ) => {
209- // regex search modifies the line buffer
209+ // regex search modifies the line cache
210210 // if the previous search was regex we need to clear it
211211 if ( wasLastSearchRegex === true ) {
212- console . log ( "destroying cache" )
213212 this . _destroyLinesCache ( ) ;
214213 }
215- this . _cancelSearchSignal = { value :false } ;
214+ this . _cancelSearchSignal = { value : false } ;
216215 this . _searchCompleted = false ;
217216 this . clearDecorations ( true ) ;
218217 this . _matches = [ ] ;
@@ -265,73 +264,85 @@ export class SearchAddon extends Disposable implements ITerminalAddon , ISearchA
265264 this . _fireResults ( ) ;
266265 }
267266
268- private _findAllMatches ( term : string , cancelSearchSignal : CancelSearchSignal ) : void {
267+ private _findAllMatches ( term : string , cancelSearchSignal : ICancelSearchSignal ) : void {
269268
270269
271- const chunckSearchIterator = this . _chunckSearchGenerator ( term , cancelSearchSignal ) ;
272- this . _iterate ( chunckSearchIterator , 0 ) ;
270+ const chunkSearchIterator = this . _chunkSearchGenerator ( term , cancelSearchSignal ) ;
271+ this . _iterate ( chunkSearchIterator , 0 ) ;
273272 }
274273
275- private _iterate ( searchIterator : Generator < { direction : string , chunckSize : number } > , chunckIndex : number ) : void {
274+ /**
275+ * @param searchIterator
276+ * @param chunkIndex only used to select first match when first chunk comes in
277+ */
278+ private _iterate ( searchIterator : Generator < { direction : string , chunkSize : number } > , chunkIndex : number ) : void {
276279 setTimeout ( ( ) => {
280+
277281 const iteratorResult = searchIterator . next ( ) ;
278- if ( chunckIndex === 0 ) {
282+
283+ if ( chunkIndex === 0 ) {
279284 this . _moveToTheNextMatch ( false ) ;
280285 }
286+
281287 if ( iteratorResult . done === false ) {
282- const { direction, chunckSize } = iteratorResult . value ;
283- const startIndex = direction === 'down' ? this . _matches . length - chunckSize : 0 ;
284- const endIndex = direction === 'down' ? this . _matches . length : chunckSize ;
285- this . _highlightChunck ( startIndex , endIndex ) ;
288+ const { direction, chunkSize } = iteratorResult . value ;
289+
290+ const startIndex = direction === 'down' ? this . _matches . length - chunkSize : 0 ;
291+ const endIndex = direction === 'down' ? this . _matches . length : chunkSize ;
292+
293+ this . _highlightChunk ( startIndex , endIndex ) ;
294+ // adjust match index with the growing result
286295 if ( direction === 'up' ) {
287- this . _currentMatchIndex += chunckSize ;
296+ this . _currentMatchIndex += chunkSize ;
288297 this . _fireResults ( ) ;
289298 }
290- this . _iterate ( searchIterator , ++ chunckIndex ) ;
299+ this . _iterate ( searchIterator , ++ chunkIndex ) ;
291300 }
292- else if ( iteratorResult . value !== false ) {
293- const { direction, chunckSize } = iteratorResult . value ;
294- const startIndex = direction === 'down' ? this . _matches . length - chunckSize : 0 ;
295- const endIndex = direction === 'down' ? this . _matches . length : chunckSize ;
296- this . _highlightChunck ( startIndex , endIndex ) ;
301+ else if ( iteratorResult . value !== false ) { // search finished without being cancelled
302+ const { direction, chunkSize } = iteratorResult . value ;
303+
304+ const startIndex = direction === 'down' ? this . _matches . length - chunkSize : 0 ;
305+ const endIndex = direction === 'down' ? this . _matches . length : chunkSize ;
306+
307+ this . _highlightChunk ( startIndex , endIndex ) ;
308+
297309 if ( direction === 'up' ) {
298- this . _currentMatchIndex += chunckSize ;
310+ this . _currentMatchIndex += chunkSize ;
299311 }
300312 this . _searchCompleted = true ;
301313 this . _fireResults ( ) ;
302314 }
303315
304- } , this . _timeBetweenChunckOperations ) ;
316+ } , this . _timeBetweenChunkOperations ) ;
305317 }
306318 private _fireResults ( ) : void {
307319 if ( this . _searchOptions ?. decorations ) {
308320 this . _onDidChangeResults . fire ( { resultIndex :this . _currentMatchIndex , resultCount : this . _matches . length , searchCompleted : this . _searchCompleted } ) ;
309321 }
310322 }
311- private * _chunckSearchGenerator ( term : string , cancelSearchSignal : CancelSearchSignal ) : Generator < { direction : string , chunckSize : number } > {
323+ private * _chunkSearchGenerator ( term : string , cancelSearchSignal : ICancelSearchSignal ) : Generator < { direction : string , chunkSize : number } > {
312324
313325 const rowIndex = this . _terminal ! . buffer . active . viewportY ;
314326
315- let searchDirection : ChunckSearchDirection = 'down' ;
327+ let searchDirection : ChunkSearchDirection = 'down' ;
316328
317329 let downDirectionLastResult = this . _find ( term , rowIndex , 0 , 'down' ) ;
318330 let upDirectionLastResult = this . _find ( term , rowIndex - 1 , this . _terminal ! . cols , 'up' ) ;
319331
320332
321333 searchDirection = downDirectionLastResult !== undefined ? 'down' : 'up' ;
322334
323- let currentChunckMatches : ISearchResult [ ] = [ ] ;
335+ let currentChunkMatches : ISearchResult [ ] = [ ] ;
324336
325337 while ( downDirectionLastResult !== undefined || upDirectionLastResult !== undefined ) {
326338
327339 if ( cancelSearchSignal . value === true ) {
328340 return false ;
329341 }
330342
331-
332343 if ( downDirectionLastResult !== undefined && searchDirection === 'down' ) {
333344
334- currentChunckMatches . push ( downDirectionLastResult ) ;
345+ currentChunkMatches . push ( downDirectionLastResult ) ;
335346
336347 downDirectionLastResult = this . _find (
337348 term ,
@@ -340,44 +351,54 @@ export class SearchAddon extends Disposable implements ITerminalAddon , ISearchA
340351 downDirectionLastResult . col + downDirectionLastResult . term . length >= this . _terminal ! . cols ? 0 : downDirectionLastResult ! . col + 1 ,
341352 'down'
342353 ) ;
354+
343355 } else if ( upDirectionLastResult !== undefined && searchDirection === 'up' ) {
344- currentChunckMatches . push ( upDirectionLastResult ) ;
356+
357+ currentChunkMatches . push ( upDirectionLastResult ) ;
358+
345359 upDirectionLastResult = this . _find (
346360 term ,
347- // using previous term length will cause problems with regex
348361 upDirectionLastResult . row ,
349362 upDirectionLastResult . col - 1 ,
350363 'up'
351364 ) ;
352365 }
353366
354- if ( this . _matches . length + currentChunckMatches . length >= this . _highlightLimit ) {
367+ if ( this . _matches . length + currentChunkMatches . length >= this . _highlightLimit ) {
368+
355369 if ( searchDirection === 'down' ) {
356- this . _matches . push ( ...currentChunckMatches ) ;
370+ this . _matches . push ( ...currentChunkMatches ) ;
371+
357372 } else {
358- currentChunckMatches . reverse ( ) ;
359- this . _matches . unshift ( ...currentChunckMatches ) ; // horible for performance just used temoprarly
373+ currentChunkMatches . reverse ( ) ;
374+ this . _matches . unshift ( ...currentChunkMatches ) ; // bad for performance just used temoprarly
375+
360376 }
361377
362- const doneReturn = { direction :searchDirection , chunckSize :currentChunckMatches . length } ;
363- currentChunckMatches = [ ] ;
378+ const doneReturn = { direction :searchDirection , chunkSize :currentChunkMatches . length } ;
379+
380+ currentChunkMatches = [ ] ;
381+
364382 return doneReturn ;
365383 }
366384
367385 if (
368- ( currentChunckMatches . length > 0 && currentChunckMatches . length % this . _chunckSize === 0 ) ||
369- ( downDirectionLastResult === undefined && searchDirection === 'down' ) ||
370- ( upDirectionLastResult === undefined && searchDirection === 'up' )
371- ) {
386+ ( currentChunkMatches . length > 0 && currentChunkMatches . length % this . _chunkSize === 0 ) ||
387+ ( downDirectionLastResult === undefined && searchDirection === 'down' ) ||
388+ ( upDirectionLastResult === undefined && searchDirection === 'up' )
389+ )
390+ {
372391 if ( searchDirection === 'down' ) {
373- this . _matches . push ( ...currentChunckMatches ) ;
392+ this . _matches . push ( ...currentChunkMatches ) ;
393+
374394 } else {
375- currentChunckMatches . reverse ( ) ;
376- this . _matches . unshift ( ...currentChunckMatches ) ; // horible for performance just used temoprarly
395+ currentChunkMatches . reverse ( ) ;
396+ this . _matches . unshift ( ...currentChunkMatches ) ; // bad for performance just used temoprarly
397+
377398 }
378399
379- const yieldReturn = { direction :searchDirection , chunckSize : currentChunckMatches . length } ;
380- currentChunckMatches = [ ] ;
400+ const yieldReturn = { direction :searchDirection , chunkSize : currentChunkMatches . length } ;
401+ currentChunkMatches = [ ] ;
381402 yield yieldReturn ;
382403
383404 searchDirection = searchDirection === 'down' ? 'up' :'down' ;
@@ -388,12 +409,13 @@ export class SearchAddon extends Disposable implements ITerminalAddon , ISearchA
388409 return true ;
389410 }
390411
391- private _highlightChunck ( startIndex : number , endIndex : number ) : void {
412+ private _highlightChunk ( startIndex : number , endIndex : number ) : void {
392413
393414 for ( let i = startIndex ; i < endIndex ; i ++ ) {
394415
395416 const match = this . _matches [ i ] ;
396417 const decoration = this . _createResultDecoration ( match ) ;
418+
397419 if ( decoration ) {
398420 this . _highlightedLines . add ( decoration . marker . line ) ;
399421 this . _matchesWithHighlightApplied . push ( { decoration, match, dispose ( ) { decoration . dispose ( ) ; } } ) ;
@@ -403,7 +425,7 @@ export class SearchAddon extends Disposable implements ITerminalAddon , ISearchA
403425 }
404426
405427
406- private _find ( term : string , startRow : number , startCol : number , direction : ChunckSearchDirection ) : ISearchResult | undefined {
428+ private _find ( term : string , startRow : number , startCol : number , direction : ChunkSearchDirection ) : ISearchResult | undefined {
407429 if ( ! this . _terminal || ! term || term . length === 0 ) {
408430 return undefined ;
409431 }
@@ -437,8 +459,8 @@ export class SearchAddon extends Disposable implements ITerminalAddon , ISearchA
437459 let resultAtOtherRowsScanColumnsRightToLeft : ISearchResult | undefined = undefined ;
438460
439461 if ( resultAtRowAndToTheLeftOfColumn === undefined ) {
440-
441- for ( let y = this . _searchOptions ?. regex === true ? startRow : startRow - 1 ; y >= 0 ; y -- ) {
462+ const startFrom = this . _searchOptions ?. regex === true ? startRow : startRow - 1 ;
463+ for ( let y = startFrom ; y >= 0 ; y -- ) {
442464 for ( let j = this . _terminal ! . cols ; j >= 0 ; j -- ) {
443465 resultAtOtherRowsScanColumnsRightToLeft = this . _findInLine ( term , { startRow : y , startCol : j } , true ) ;
444466 if ( resultAtOtherRowsScanColumnsRightToLeft ) {
@@ -470,41 +492,35 @@ export class SearchAddon extends Disposable implements ITerminalAddon , ISearchA
470492 const row = searchPosition . startRow ;
471493 const col = searchPosition . startCol ;
472494
473- // console.log( translateBufferLineToStringWithWrap(terminal,row, true));
474- // // Ignore wrapped lines, only consider on unwrapped line (first row of command string).
475- // if ( terminal.buffer.active.getLine(row)?.isWrapped === true) {
476- // // console.log("ignored a wrapped line")
477- // // return;
478- // }
479-
480495 let cache = this . _linesCache ?. [ row ] ;
481496 if ( ! cache ) {
482497 cache = translateBufferLineToStringWithWrap ( terminal , row , true ) ;
483- // console.log("is wrapped: " + (terminal.buffer.active.getLine(row)?.isWrapped === true))
484- // console.log("string line: "+cache[0]+" string length:"+cache[0].length +" offset: "+cache[1]);
485- if ( this . _linesCache ) {
486- this . _linesCache [ row ] = cache ;
487- }
498+ this . _linesCache [ row ] = cache ;
488499 }
489500 const [ stringLine , offsets ] = cache ;
490501
491502 let offset = bufferColsToStringOffset ( terminal , row , col ) ;
492- // console.log("direction "+scanRightToLeft+" rows "+row+" "+"column: "+col+" offset"+offset+" total view port cols" +this._terminal!.cols);
493- const searchTerm = this . _searchOptions ?. caseSensitive ? term : term . toLowerCase ( ) ;
494- const searchStringLine = this . _searchOptions ?. caseSensitive ? stringLine : stringLine . toLowerCase ( ) ;
495503
496504 if ( offset > stringLine . length ) {
497505 offset = stringLine . length ;
498506 }
507+
508+ const searchTerm = this . _searchOptions ?. caseSensitive ? term : term . toLowerCase ( ) ;
509+ const searchStringLine = this . _searchOptions ?. caseSensitive ? stringLine : stringLine . toLowerCase ( ) ;
510+
499511 let resultIndex = - 1 ;
512+
500513 if ( this . _searchOptions ?. regex ) {
514+
501515 const searchRegex = RegExp ( searchTerm , 'g' ) ;
516+
502517 if ( scanRightToLeft === false ) {
503518 const foundTerm : RegExpExecArray | null = searchRegex . exec ( searchStringLine . slice ( offset ) ) ;
504519 if ( foundTerm && foundTerm [ 0 ] . length > 0 ) {
505520 resultIndex = offset + ( searchRegex . lastIndex - foundTerm [ 0 ] . length ) ;
506521 term = foundTerm [ 0 ] ;
507522 }
523+
508524 } else {
509525 const foundTerm : RegExpExecArray | null = searchRegex . exec ( searchStringLine . slice ( offset ) ) ;
510526 if ( foundTerm && foundTerm [ 0 ] . length > 0 ) {
0 commit comments