@xterm/addon-search 0.16.0-beta.124 → 0.16.0-beta.125

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xterm/addon-search",
3
- "version": "0.16.0-beta.124",
3
+ "version": "0.16.0-beta.125",
4
4
  "author": {
5
5
  "name": "The xterm.js authors",
6
6
  "url": "https://xtermjs.org/"
@@ -22,7 +22,7 @@
22
22
  "start": "node ../../demo/start"
23
23
  },
24
24
  "peerDependencies": {
25
- "@xterm/xterm": "^5.6.0-beta.124"
25
+ "@xterm/xterm": "^5.6.0-beta.125"
26
26
  },
27
- "commit": "02b3704af0b1823ccb4ebb6c5fa4059a7934dbd8"
27
+ "commit": "15d90826f90ffbd45d1cd8b27bebc665a152cec6"
28
28
  }
@@ -6,7 +6,8 @@
6
6
  import type { Terminal, IDisposable, ITerminalAddon, IDecoration } from '@xterm/xterm';
7
7
  import type { SearchAddon as ISearchApi, ISearchOptions, ISearchDecorationOptions } from '@xterm/addon-search';
8
8
  import { 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';
10
11
 
11
12
  interface IInternalSearchOptions {
12
13
  noScroll?: boolean;
@@ -33,16 +34,7 @@ export interface ISearchResult {
33
34
  size: number;
34
35
  }
35
36
 
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
+
46
38
 
47
39
  interface IHighlight extends IDisposable {
48
40
  decoration: IDecoration;
@@ -65,11 +57,7 @@ const enum Constants {
65
57
  */
66
58
  NON_WORD_CHARACTERS = ' ~!@#$%^&*()+`-=[]{}|\\;:"\',./<>?',
67
59
 
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
+
73
61
 
74
62
  /**
75
63
  * Default maximum number of search results to highlight simultaneously. This limit prevents
@@ -89,14 +77,7 @@ export class SearchAddon extends Disposable implements ITerminalAddon, ISearchAp
89
77
  private _highlightLimit: number;
90
78
  private _lastSearchOptions: ISearchOptions | undefined;
91
79
  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>());
100
81
 
101
82
  private readonly _onDidChangeResults = this._register(new Emitter<ISearchResultChangeEvent>());
102
83
  public get onDidChangeResults(): Event<ISearchResultChangeEvent> { return this._onDidChangeResults.event; }
@@ -109,6 +90,7 @@ export class SearchAddon extends Disposable implements ITerminalAddon, ISearchAp
109
90
 
110
91
  public activate(terminal: Terminal): void {
111
92
  this._terminal = terminal;
93
+ this._lineCache.value = new SearchLineCache(terminal);
112
94
  this._register(this._terminal.onWriteParsed(() => this._updateMatches()));
113
95
  this._register(this._terminal.onResize(() => this._updateMatches()));
114
96
  this._register(toDisposable(() => this.clearDecorations()));
@@ -223,7 +205,7 @@ export class SearchAddon extends Disposable implements ITerminalAddon, ISearchAp
223
205
 
224
206
  let result: ISearchResult | undefined = undefined;
225
207
 
226
- this._initLinesCache();
208
+ this._lineCache.value!.initLinesCache();
227
209
 
228
210
  const searchPosition: ISearchPosition = {
229
211
  startRow,
@@ -271,7 +253,7 @@ export class SearchAddon extends Disposable implements ITerminalAddon, ISearchAp
271
253
  }
272
254
  }
273
255
 
274
- this._initLinesCache();
256
+ this._lineCache.value!.initLinesCache();
275
257
 
276
258
  const searchPosition: ISearchPosition = {
277
259
  startRow,
@@ -392,7 +374,7 @@ export class SearchAddon extends Disposable implements ITerminalAddon, ISearchAp
392
374
  let startCol = this._terminal.cols;
393
375
  const isReverseSearch = true;
394
376
 
395
- this._initLinesCache();
377
+ this._lineCache.value!.initLinesCache();
396
378
  const searchPosition: ISearchPosition = {
397
379
  startRow,
398
380
  startCol
@@ -443,32 +425,7 @@ export class SearchAddon extends Disposable implements ITerminalAddon, ISearchAp
443
425
  return this._selectResult(result, searchOptions?.decorations, internalSearchOptions?.noScroll);
444
426
  }
445
427
 
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
- }
459
428
 
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
- }
472
429
 
473
430
  /**
474
431
  * 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
513
470
  searchPosition.startCol += terminal.cols;
514
471
  return this._findInLine(term, searchPosition, searchOptions);
515
472
  }
516
- let cache = this._linesCache?.[row];
473
+ let cache = this._lineCache.value!.getLineFromCache(row);
517
474
  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);
522
477
  }
523
478
  const [stringLine, offsets] = cache;
524
479
 
@@ -639,42 +594,7 @@ export class SearchAddon extends Disposable implements ITerminalAddon, ISearchAp
639
594
  return offset;
640
595
  }
641
596
 
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
+
678
598
 
679
599
  /**
680
600
  * Selects and scrolls to a result.
@@ -0,0 +1,117 @@
1
+ /**
2
+ * Copyright (c) 2017 The xterm.js authors. All rights reserved.
3
+ * @license MIT
4
+ */
5
+
6
+ import type { Terminal } from '@xterm/xterm';
7
+ import { combinedDisposable, Disposable, MutableDisposable, toDisposable } from 'vs/base/common/lifecycle';
8
+
9
+ export type LineCacheEntry = [
10
+ /**
11
+ * The string representation of a line (as opposed to the buffer cell representation).
12
+ */
13
+ lineAsString: string,
14
+ /**
15
+ * The offsets where each line starts when the entry describes a wrapped line.
16
+ */
17
+ lineOffsets: number[]
18
+ ];
19
+
20
+ /**
21
+ * Configuration constants for the search line cache functionality.
22
+ */
23
+ const enum Constants {
24
+ /**
25
+ * Time-to-live for cached search results in milliseconds. After this duration, cached search
26
+ * results will be invalidated to ensure they remain consistent with terminal content changes.
27
+ */
28
+ LINES_CACHE_TIME_TO_LIVE = 15000
29
+ }
30
+
31
+ export class SearchLineCache extends Disposable {
32
+ /**
33
+ * translateBufferLineToStringWithWrap is a fairly expensive call.
34
+ * We memoize the calls into an array that has a time based ttl.
35
+ * _linesCache is also invalidated when the terminal cursor moves.
36
+ */
37
+ private _linesCache: LineCacheEntry[] | undefined;
38
+ private _linesCacheTimeoutId = 0;
39
+ private _linesCacheDisposables = this._register(new MutableDisposable());
40
+
41
+ constructor(private _terminal: Terminal) {
42
+ super();
43
+ this._register(toDisposable(() => this._destroyLinesCache()));
44
+ }
45
+
46
+ /**
47
+ * Sets up a line cache with a ttl
48
+ */
49
+ public initLinesCache(): void {
50
+ if (!this._linesCache) {
51
+ this._linesCache = new Array(this._terminal.buffer.active.length);
52
+ this._linesCacheDisposables.value = combinedDisposable(
53
+ this._terminal.onLineFeed(() => this._destroyLinesCache()),
54
+ this._terminal.onCursorMove(() => this._destroyLinesCache()),
55
+ this._terminal.onResize(() => this._destroyLinesCache())
56
+ );
57
+ }
58
+
59
+ window.clearTimeout(this._linesCacheTimeoutId);
60
+ this._linesCacheTimeoutId = window.setTimeout(() => this._destroyLinesCache(), Constants.LINES_CACHE_TIME_TO_LIVE);
61
+ }
62
+
63
+ private _destroyLinesCache(): void {
64
+ this._linesCache = undefined;
65
+ this._linesCacheDisposables.clear();
66
+ if (this._linesCacheTimeoutId) {
67
+ window.clearTimeout(this._linesCacheTimeoutId);
68
+ this._linesCacheTimeoutId = 0;
69
+ }
70
+ }
71
+
72
+ public getLineFromCache(row: number): LineCacheEntry | undefined {
73
+ return this._linesCache?.[row];
74
+ }
75
+
76
+ public setLineInCache(row: number, entry: LineCacheEntry): void {
77
+ if (this._linesCache) {
78
+ this._linesCache[row] = entry;
79
+ }
80
+ }
81
+
82
+ /**
83
+ * Translates a buffer line to a string, including subsequent lines if they are wraps.
84
+ * Wide characters will count as two columns in the resulting string. This
85
+ * function is useful for getting the actual text underneath the raw selection
86
+ * position.
87
+ * @param lineIndex The index of the line being translated.
88
+ * @param trimRight Whether to trim whitespace to the right.
89
+ */
90
+ public translateBufferLineToStringWithWrap(lineIndex: number, trimRight: boolean): LineCacheEntry {
91
+ const strings = [];
92
+ const lineOffsets = [0];
93
+ let line = this._terminal.buffer.active.getLine(lineIndex);
94
+ while (line) {
95
+ const nextLine = this._terminal.buffer.active.getLine(lineIndex + 1);
96
+ const lineWrapsToNext = nextLine ? nextLine.isWrapped : false;
97
+ let string = line.translateToString(!lineWrapsToNext && trimRight);
98
+ if (lineWrapsToNext && nextLine) {
99
+ const lastCell = line.getCell(line.length - 1);
100
+ const lastCellIsNull = lastCell && lastCell.getCode() === 0 && lastCell.getWidth() === 1;
101
+ // a wide character wrapped to the next line
102
+ if (lastCellIsNull && nextLine.getCell(0)?.getWidth() === 2) {
103
+ string = string.slice(0, -1);
104
+ }
105
+ }
106
+ strings.push(string);
107
+ if (lineWrapsToNext) {
108
+ lineOffsets.push(lineOffsets[lineOffsets.length - 1] + string.length);
109
+ } else {
110
+ break;
111
+ }
112
+ lineIndex++;
113
+ line = nextLine;
114
+ }
115
+ return [strings.join(''), lineOffsets];
116
+ }
117
+ }