@xterm/addon-search 0.16.0-beta.123 → 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/lib/addon-search.js +1 -1
- package/lib/addon-search.js.map +1 -1
- package/lib/addon-search.mjs +8 -8
- package/lib/addon-search.mjs.map +4 -4
- package/package.json +3 -3
- package/src/SearchAddon.ts +13 -93
- package/src/SearchLineCache.ts +117 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xterm/addon-search",
|
|
3
|
-
"version": "0.16.0-beta.
|
|
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.
|
|
25
|
+
"@xterm/xterm": "^5.6.0-beta.125"
|
|
26
26
|
},
|
|
27
|
-
"commit": "
|
|
27
|
+
"commit": "15d90826f90ffbd45d1cd8b27bebc665a152cec6"
|
|
28
28
|
}
|
package/src/SearchAddon.ts
CHANGED
|
@@ -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 {
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
473
|
+
let cache = this._lineCache.value!.getLineFromCache(row);
|
|
517
474
|
if (!cache) {
|
|
518
|
-
cache = this.
|
|
519
|
-
|
|
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
|
+
}
|