@@ -58,6 +58,11 @@ interface IHighlight extends IDisposable {
5858 match : ISearchResult ;
5959}
6060
61+ interface IMultiHighlight extends IDisposable {
62+ decorations : IDecoration [ ] ;
63+ match : ISearchResult ;
64+ }
65+
6166const NON_WORD_CHARACTERS = ' ~!@#$%^&*()+`-=[]{}|\\;:"\',./<>?' ;
6267const LINES_CACHE_TIME_TO_LIVE = 15 * 1000 ; // 15 secs
6368const DEFAULT_HIGHLIGHT_LIMIT = 1000 ;
@@ -67,7 +72,7 @@ export class SearchAddon extends Disposable implements ITerminalAddon , ISearchA
6772 private _cachedSearchTerm : string | undefined ;
6873 private _highlightedLines : Set < number > = new Set ( ) ;
6974 private _highlightDecorations : IHighlight [ ] = [ ] ;
70- private _selectedDecoration : MutableDisposable < IHighlight > = this . _register ( new MutableDisposable ( ) ) ;
75+ private _selectedDecoration : MutableDisposable < IMultiHighlight > = this . _register ( new MutableDisposable ( ) ) ;
7176 private _highlightLimit : number ;
7277 private _lastSearchOptions : ISearchOptions | undefined ;
7378 private _highlightTimeout : number | undefined ;
@@ -179,14 +184,20 @@ export class SearchAddon extends Disposable implements ITerminalAddon , ISearchA
179184 ) ;
180185 }
181186 for ( const match of searchResultsWithHighlight ) {
182- const decoration = this . _createResultDecoration ( match , searchOptions . decorations ! ) ;
183- if ( decoration ) {
184- this . _highlightedLines . add ( decoration . marker . line ) ;
185- this . _highlightDecorations . push ( { decoration, match, dispose ( ) { decoration . dispose ( ) ; } } ) ;
187+ const decorations = this . _createResultDecorations ( match , searchOptions . decorations ! , false ) ;
188+ if ( decorations ) {
189+ for ( const decoration of decorations ) {
190+ this . _storeDecoration ( decoration , match ) ;
191+ }
186192 }
187193 }
188194 }
189195
196+ private _storeDecoration ( decoration : IDecoration , match : ISearchResult ) : void {
197+ this . _highlightedLines . add ( decoration . marker . line ) ;
198+ this . _highlightDecorations . push ( { decoration, match, dispose ( ) { decoration . dispose ( ) ; } } ) ;
199+ }
200+
190201 private _find ( term : string , startRow : number , startCol : number , searchOptions ?: ISearchOptions ) : ISearchResult | undefined {
191202 if ( ! this . _terminal || ! term || term . length === 0 ) {
192203 this . _terminal ?. clearSelection ( ) ;
@@ -666,25 +677,9 @@ export class SearchAddon extends Disposable implements ITerminalAddon , ISearchA
666677 }
667678 terminal . select ( result . col , result . row , result . size ) ;
668679 if ( options ) {
669- const marker = terminal . registerMarker ( - terminal . buffer . active . baseY - terminal . buffer . active . cursorY + result . row ) ;
670- if ( marker ) {
671- const decoration = terminal . registerDecoration ( {
672- marker,
673- x : result . col ,
674- width : result . size ,
675- backgroundColor : options . activeMatchBackground ,
676- layer : 'top' ,
677- overviewRulerOptions : {
678- color : options . activeMatchColorOverviewRuler
679- }
680- } ) ;
681- if ( decoration ) {
682- const disposables : IDisposable [ ] = [ ] ;
683- disposables . push ( marker ) ;
684- disposables . push ( decoration . onRender ( ( e ) => this . _applyStyles ( e , options . activeMatchBorder , true ) ) ) ;
685- disposables . push ( decoration . onDispose ( ( ) => dispose ( disposables ) ) ) ;
686- this . _selectedDecoration . value = { decoration, match : result , dispose ( ) { decoration . dispose ( ) ; } } ;
687- }
680+ const decorations = this . _createResultDecorations ( result , options , true ) ;
681+ if ( decorations ) {
682+ this . _selectedDecoration . value = { decorations, match : result , dispose ( ) { dispose ( decorations ) ; } } ;
688683 }
689684 }
690685
@@ -724,28 +719,45 @@ export class SearchAddon extends Disposable implements ITerminalAddon , ISearchA
724719 * @param options the options for the decoration
725720 * @returns the {@link IDecoration} or undefined if the marker has already been disposed of
726721 */
727- private _createResultDecoration ( result : ISearchResult , options : ISearchDecorationOptions ) : IDecoration | undefined {
722+ private _createResultDecorations ( result : ISearchResult , options : ISearchDecorationOptions , isActiveResult : boolean ) : IDecoration [ ] | undefined {
728723 const terminal = this . _terminal ! ;
729- const marker = terminal . registerMarker ( - terminal . buffer . active . baseY - terminal . buffer . active . cursorY + result . row ) ;
730- if ( ! marker ) {
731- return undefined ;
732- }
733- const findResultDecoration = terminal . registerDecoration ( {
734- marker,
735- x : result . col ,
736- width : result . size ,
737- backgroundColor : options . matchBackground ,
738- overviewRulerOptions : this . _highlightedLines . has ( marker . line ) ? undefined : {
739- color : options . matchOverviewRuler ,
740- position : 'center'
724+
725+ // Gather decoration ranges for this match as it could wrap
726+ const decorationRanges : [ number , number , number ] [ ] = [ ] ;
727+ let currentCol = result . col ;
728+ let remainingSize = result . size ;
729+ let markerOffset = - terminal . buffer . active . baseY - terminal . buffer . active . cursorY + result . row ;
730+ while ( remainingSize > 0 ) {
731+ const amountThisRow = Math . min ( terminal . cols - currentCol , remainingSize ) ;
732+ decorationRanges . push ( [ markerOffset , currentCol , amountThisRow ] ) ;
733+ currentCol = 0 ;
734+ remainingSize -= amountThisRow ;
735+ markerOffset ++ ;
736+ }
737+
738+ // Create the decorations
739+ const decorations : IDecoration [ ] = [ ] ;
740+ for ( const range of decorationRanges ) {
741+ const marker = terminal . registerMarker ( range [ 0 ] ) ;
742+ const decoration = terminal . registerDecoration ( {
743+ marker,
744+ x : range [ 1 ] ,
745+ width : range [ 2 ] ,
746+ backgroundColor : isActiveResult ? options . activeMatchBackground : options . matchBackground ,
747+ overviewRulerOptions : this . _highlightedLines . has ( marker . line ) ? undefined : {
748+ color : isActiveResult ? options . activeMatchColorOverviewRuler : options . matchOverviewRuler ,
749+ position : 'center'
750+ }
751+ } ) ;
752+ if ( decoration ) {
753+ const disposables : IDisposable [ ] = [ ] ;
754+ disposables . push ( marker ) ;
755+ disposables . push ( decoration . onRender ( ( e ) => this . _applyStyles ( e , isActiveResult ? options . activeMatchBorder : options . matchBorder , false ) ) ) ;
756+ disposables . push ( decoration . onDispose ( ( ) => dispose ( disposables ) ) ) ;
757+ decorations . push ( decoration ) ;
741758 }
742- } ) ;
743- if ( findResultDecoration ) {
744- const disposables : IDisposable [ ] = [ ] ;
745- disposables . push ( marker ) ;
746- disposables . push ( findResultDecoration . onRender ( ( e ) => this . _applyStyles ( e , options . matchBorder , false ) ) ) ;
747- disposables . push ( findResultDecoration . onDispose ( ( ) => dispose ( disposables ) ) ) ;
748- }
749- return findResultDecoration ;
759+ }
760+
761+ return decorations . length === 0 ? undefined : decorations ;
750762 }
751763}
0 commit comments