@xterm/addon-search 0.16.0-beta.11 → 0.16.0-beta.110

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,8 +5,8 @@
5
5
 
6
6
  import type { Terminal, IDisposable, ITerminalAddon, IDecoration } from '@xterm/xterm';
7
7
  import type { SearchAddon as ISearchApi } from '@xterm/addon-search';
8
- import { EventEmitter } from 'common/EventEmitter';
9
- import { Disposable, toDisposable, disposeArray, MutableDisposable, getDisposeArrayDisposable } from 'common/Lifecycle';
8
+ import { Emitter } from 'vs/base/common/event';
9
+ import { combinedDisposable, Disposable, dispose, MutableDisposable, toDisposable } from 'vs/base/common/lifecycle';
10
10
 
11
11
  export interface ISearchOptions {
12
12
  regex?: boolean;
@@ -58,6 +58,11 @@ interface IHighlight extends IDisposable {
58
58
  match: ISearchResult;
59
59
  }
60
60
 
61
+ interface IMultiHighlight extends IDisposable {
62
+ decorations: IDecoration[];
63
+ match: ISearchResult;
64
+ }
65
+
61
66
  const NON_WORD_CHARACTERS = ' ~!@#$%^&*()+`-=[]{}|\\;:"\',./<>?';
62
67
  const LINES_CACHE_TIME_TO_LIVE = 15 * 1000; // 15 secs
63
68
  const DEFAULT_HIGHLIGHT_LIMIT = 1000;
@@ -67,7 +72,7 @@ export class SearchAddon extends Disposable implements ITerminalAddon , ISearchA
67
72
  private _cachedSearchTerm: string | undefined;
68
73
  private _highlightedLines: Set<number> = new Set();
69
74
  private _highlightDecorations: IHighlight[] = [];
70
- private _selectedDecoration: MutableDisposable<IHighlight> = this.register(new MutableDisposable());
75
+ private _selectedDecoration: MutableDisposable<IMultiHighlight> = this._register(new MutableDisposable());
71
76
  private _highlightLimit: number;
72
77
  private _lastSearchOptions: ISearchOptions | undefined;
73
78
  private _highlightTimeout: number | undefined;
@@ -80,7 +85,7 @@ export class SearchAddon extends Disposable implements ITerminalAddon , ISearchA
80
85
  private _linesCacheTimeoutId = 0;
81
86
  private _linesCacheDisposables = new MutableDisposable();
82
87
 
83
- private readonly _onDidChangeResults = this.register(new EventEmitter<{ resultIndex: number, resultCount: number }>());
88
+ private readonly _onDidChangeResults = this._register(new Emitter<{ resultIndex: number, resultCount: number }>());
84
89
  public readonly onDidChangeResults = this._onDidChangeResults.event;
85
90
 
86
91
  constructor(options?: Partial<ISearchAddonOptions>) {
@@ -91,9 +96,9 @@ export class SearchAddon extends Disposable implements ITerminalAddon , ISearchA
91
96
 
92
97
  public activate(terminal: Terminal): void {
93
98
  this._terminal = terminal;
94
- this.register(this._terminal.onWriteParsed(() => this._updateMatches()));
95
- this.register(this._terminal.onResize(() => this._updateMatches()));
96
- this.register(toDisposable(() => this.clearDecorations()));
99
+ this._register(this._terminal.onWriteParsed(() => this._updateMatches()));
100
+ this._register(this._terminal.onResize(() => this._updateMatches()));
101
+ this._register(toDisposable(() => this.clearDecorations()));
97
102
  }
98
103
 
99
104
  private _updateMatches(): void {
@@ -111,7 +116,7 @@ export class SearchAddon extends Disposable implements ITerminalAddon , ISearchA
111
116
 
112
117
  public clearDecorations(retainCachedSearchTerm?: boolean): void {
113
118
  this._selectedDecoration.clear();
114
- disposeArray(this._highlightDecorations);
119
+ dispose(this._highlightDecorations);
115
120
  this._highlightDecorations = [];
116
121
  this._highlightedLines.clear();
117
122
  if (!retainCachedSearchTerm) {
@@ -179,14 +184,20 @@ export class SearchAddon extends Disposable implements ITerminalAddon , ISearchA
179
184
  );
180
185
  }
181
186
  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
+ }
186
192
  }
187
193
  }
188
194
  }
189
195
 
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
+
190
201
  private _find(term: string, startRow: number, startCol: number, searchOptions?: ISearchOptions): ISearchResult | undefined {
191
202
  if (!this._terminal || !term || term.length === 0) {
192
203
  this._terminal?.clearSelection();
@@ -426,11 +437,11 @@ export class SearchAddon extends Disposable implements ITerminalAddon , ISearchA
426
437
  const terminal = this._terminal!;
427
438
  if (!this._linesCache) {
428
439
  this._linesCache = new Array(terminal.buffer.active.length);
429
- this._linesCacheDisposables.value = getDisposeArrayDisposable([
440
+ this._linesCacheDisposables.value = combinedDisposable(
430
441
  terminal.onLineFeed(() => this._destroyLinesCache()),
431
442
  terminal.onCursorMove(() => this._destroyLinesCache()),
432
443
  terminal.onResize(() => this._destroyLinesCache())
433
- ]);
444
+ );
434
445
  }
435
446
 
436
447
  window.clearTimeout(this._linesCacheTimeoutId);
@@ -499,12 +510,16 @@ export class SearchAddon extends Disposable implements ITerminalAddon , ISearchA
499
510
  const [stringLine, offsets] = cache;
500
511
 
501
512
  const offset = this._bufferColsToStringOffset(row, col);
502
- const searchTerm = searchOptions.caseSensitive ? term : term.toLowerCase();
503
- const searchStringLine = searchOptions.caseSensitive ? stringLine : stringLine.toLowerCase();
513
+ let searchTerm = term;
514
+ let searchStringLine = stringLine;
515
+ if (!searchOptions.regex) {
516
+ searchTerm = searchOptions.caseSensitive ? term : term.toLowerCase();
517
+ searchStringLine = searchOptions.caseSensitive ? stringLine : stringLine.toLowerCase();
518
+ }
504
519
 
505
520
  let resultIndex = -1;
506
521
  if (searchOptions.regex) {
507
- const searchRegex = RegExp(searchTerm, 'g');
522
+ const searchRegex = RegExp(searchTerm, searchOptions.caseSensitive ? 'g' : 'gi');
508
523
  let foundTerm: RegExpExecArray | null;
509
524
  if (isReverseSearch) {
510
525
  // This loop will get the resultIndex of the _last_ regex match in the range 0..offset
@@ -662,25 +677,9 @@ export class SearchAddon extends Disposable implements ITerminalAddon , ISearchA
662
677
  }
663
678
  terminal.select(result.col, result.row, result.size);
664
679
  if (options) {
665
- const marker = terminal.registerMarker(-terminal.buffer.active.baseY - terminal.buffer.active.cursorY + result.row);
666
- if (marker) {
667
- const decoration = terminal.registerDecoration({
668
- marker,
669
- x: result.col,
670
- width: result.size,
671
- backgroundColor: options.activeMatchBackground,
672
- layer: 'top',
673
- overviewRulerOptions: {
674
- color: options.activeMatchColorOverviewRuler
675
- }
676
- });
677
- if (decoration) {
678
- const disposables: IDisposable[] = [];
679
- disposables.push(marker);
680
- disposables.push(decoration.onRender((e) => this._applyStyles(e, options.activeMatchBorder, true)));
681
- disposables.push(decoration.onDispose(() => disposeArray(disposables)));
682
- this._selectedDecoration.value = { decoration, match: result, dispose() { decoration.dispose(); } };
683
- }
680
+ const decorations = this._createResultDecorations(result, options, true);
681
+ if (decorations) {
682
+ this._selectedDecoration.value = { decorations, match: result, dispose() { dispose(decorations); } };
684
683
  }
685
684
  }
686
685
 
@@ -720,28 +719,45 @@ export class SearchAddon extends Disposable implements ITerminalAddon , ISearchA
720
719
  * @param options the options for the decoration
721
720
  * @returns the {@link IDecoration} or undefined if the marker has already been disposed of
722
721
  */
723
- private _createResultDecoration(result: ISearchResult, options: ISearchDecorationOptions): IDecoration | undefined {
722
+ private _createResultDecorations(result: ISearchResult, options: ISearchDecorationOptions, isActiveResult: boolean): IDecoration[] | undefined {
724
723
  const terminal = this._terminal!;
725
- const marker = terminal.registerMarker(-terminal.buffer.active.baseY - terminal.buffer.active.cursorY + result.row);
726
- if (!marker) {
727
- return undefined;
728
- }
729
- const findResultDecoration = terminal.registerDecoration({
730
- marker,
731
- x: result.col,
732
- width: result.size,
733
- backgroundColor: options.matchBackground,
734
- overviewRulerOptions: this._highlightedLines.has(marker.line) ? undefined : {
735
- color: options.matchOverviewRuler,
736
- 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);
737
758
  }
738
- });
739
- if (findResultDecoration) {
740
- const disposables: IDisposable[] = [];
741
- disposables.push(marker);
742
- disposables.push(findResultDecoration.onRender((e) => this._applyStyles(e, options.matchBorder, false)));
743
- disposables.push(findResultDecoration.onDispose(() => disposeArray(disposables)));
744
- }
745
- return findResultDecoration;
759
+ }
760
+
761
+ return decorations.length === 0 ? undefined : decorations;
746
762
  }
747
763
  }