Skip to content

Commit bffdc59

Browse files
authored
Merge branch 'master' into tyriar/search-refactor
2 parents 76a8a8e + e37aba5 commit bffdc59

File tree

10 files changed

+252
-29
lines changed

10 files changed

+252
-29
lines changed

addons/addon-ligatures/yarn.lock

Lines changed: 107 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,14 @@ buffer-crc32@~0.2.3:
6363
version "0.2.13"
6464
resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242"
6565

66+
call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2:
67+
version "1.0.2"
68+
resolved "https://registry.yarnpkg.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz#4b5428c222be985d79c3d82657479dbe0b59b2d6"
69+
integrity sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==
70+
dependencies:
71+
es-errors "^1.3.0"
72+
function-bind "^1.1.2"
73+
6674
combined-stream@^1.0.8:
6775
version "1.0.8"
6876
resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
@@ -79,6 +87,42 @@ diff@^3.5.0:
7987
version "3.5.0"
8088
resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12"
8189

90+
dunder-proto@^1.0.1:
91+
version "1.0.1"
92+
resolved "https://registry.yarnpkg.com/dunder-proto/-/dunder-proto-1.0.1.tgz#d7ae667e1dc83482f8b70fd0f6eefc50da30f58a"
93+
integrity sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==
94+
dependencies:
95+
call-bind-apply-helpers "^1.0.1"
96+
es-errors "^1.3.0"
97+
gopd "^1.2.0"
98+
99+
es-define-property@^1.0.1:
100+
version "1.0.1"
101+
resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.1.tgz#983eb2f9a6724e9303f61addf011c72e09e0b0fa"
102+
integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==
103+
104+
es-errors@^1.3.0:
105+
version "1.3.0"
106+
resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f"
107+
integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==
108+
109+
es-object-atoms@^1.0.0, es-object-atoms@^1.1.1:
110+
version "1.1.1"
111+
resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz#1c4f2c4837327597ce69d2ca190a7fdd172338c1"
112+
integrity sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==
113+
dependencies:
114+
es-errors "^1.3.0"
115+
116+
es-set-tostringtag@^2.1.0:
117+
version "2.1.0"
118+
resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz#f31dbbe0c183b00a6d26eb6325c810c0fd18bd4d"
119+
integrity sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==
120+
dependencies:
121+
es-errors "^1.3.0"
122+
get-intrinsic "^1.2.6"
123+
has-tostringtag "^1.0.2"
124+
hasown "^2.0.2"
125+
82126
fd-slicer@~1.1.0:
83127
version "1.1.0"
84128
resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e"
@@ -115,22 +159,77 @@ font-ligatures@^1.4.1:
115159
opentype.js "^0.8.0"
116160

117161
form-data@^4.0.0:
118-
version "4.0.0"
119-
resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452"
120-
integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==
162+
version "4.0.4"
163+
resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.4.tgz#784cdcce0669a9d68e94d11ac4eea98088edd2c4"
164+
integrity sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==
121165
dependencies:
122166
asynckit "^0.4.0"
123167
combined-stream "^1.0.8"
168+
es-set-tostringtag "^2.1.0"
169+
hasown "^2.0.2"
124170
mime-types "^2.1.12"
125171

172+
function-bind@^1.1.2:
173+
version "1.1.2"
174+
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c"
175+
integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==
176+
177+
get-intrinsic@^1.2.6:
178+
version "1.3.0"
179+
resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz#743f0e3b6964a93a5491ed1bffaae054d7f98d01"
180+
integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==
181+
dependencies:
182+
call-bind-apply-helpers "^1.0.2"
183+
es-define-property "^1.0.1"
184+
es-errors "^1.3.0"
185+
es-object-atoms "^1.1.1"
186+
function-bind "^1.1.2"
187+
get-proto "^1.0.1"
188+
gopd "^1.2.0"
189+
has-symbols "^1.1.0"
190+
hasown "^2.0.2"
191+
math-intrinsics "^1.1.0"
192+
193+
get-proto@^1.0.1:
194+
version "1.0.1"
195+
resolved "https://registry.yarnpkg.com/get-proto/-/get-proto-1.0.1.tgz#150b3f2743869ef3e851ec0c49d15b1d14d00ee1"
196+
integrity sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==
197+
dependencies:
198+
dunder-proto "^1.0.1"
199+
es-object-atoms "^1.0.0"
200+
126201
get-system-fonts@^2.0.0:
127202
version "2.0.0"
128203
resolved "https://registry.yarnpkg.com/get-system-fonts/-/get-system-fonts-2.0.0.tgz#a43b9a33f05c0715a60176d2aad5ce6e98f0a3c6"
129204

205+
gopd@^1.2.0:
206+
version "1.2.0"
207+
resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1"
208+
integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==
209+
130210
has-flag@^3.0.0:
131211
version "3.0.0"
132212
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
133213

214+
has-symbols@^1.0.3, has-symbols@^1.1.0:
215+
version "1.1.0"
216+
resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.1.0.tgz#fc9c6a783a084951d0b971fe1018de813707a338"
217+
integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==
218+
219+
has-tostringtag@^1.0.2:
220+
version "1.0.2"
221+
resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc"
222+
integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==
223+
dependencies:
224+
has-symbols "^1.0.3"
225+
226+
hasown@^2.0.2:
227+
version "2.0.2"
228+
resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003"
229+
integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==
230+
dependencies:
231+
function-bind "^1.1.2"
232+
134233
135234
version "0.0.1"
136235
resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf"
@@ -168,6 +267,11 @@ lru-cache@^6.0.0:
168267
dependencies:
169268
yallist "^4.0.0"
170269

270+
math-intrinsics@^1.1.0:
271+
version "1.1.0"
272+
resolved "https://registry.yarnpkg.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9"
273+
integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==
274+
171275
172276
version "1.52.0"
173277
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"

addons/addon-search/src/SearchAddon.ts

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
import type { Terminal, IDisposable, ITerminalAddon, IDecoration } from '@xterm/xterm';
77
import type { SearchAddon as ISearchApi, ISearchOptions, ISearchDecorationOptions } from '@xterm/addon-search';
8-
import { Emitter } from 'vs/base/common/event';
8+
import { Emitter, Event } from 'vs/base/common/event';
99
import { combinedDisposable, Disposable, dispose, MutableDisposable, toDisposable } from 'vs/base/common/lifecycle';
1010

1111
interface IInternalSearchOptions {
@@ -17,6 +17,11 @@ export interface ISearchPosition {
1717
startRow: number;
1818
}
1919

20+
export interface ISearchResultChangeEvent {
21+
resultIndex: number;
22+
resultCount: number;
23+
}
24+
2025
export interface ISearchAddonOptions {
2126
highlightLimit: number;
2227
}
@@ -49,11 +54,32 @@ interface IMultiHighlight extends IDisposable {
4954
match: ISearchResult;
5055
}
5156

52-
const NON_WORD_CHARACTERS = ' ~!@#$%^&*()+`-=[]{}|\\;:"\',./<>?';
53-
const LINES_CACHE_TIME_TO_LIVE = 15 * 1000; // 15 secs
54-
const DEFAULT_HIGHLIGHT_LIMIT = 1000;
57+
/**
58+
* Configuration constants for the search addon functionality.
59+
*/
60+
const enum Constants {
61+
/**
62+
* Characters that are considered non-word characters for search boundary detection. These
63+
* characters are used to determine word boundaries when performing whole-word searches. Includes
64+
* common punctuation, symbols, and whitespace characters.
65+
*/
66+
NON_WORD_CHARACTERS = ' ~!@#$%^&*()+`-=[]{}|\\;:"\',./<>?',
67+
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,
73+
74+
/**
75+
* Default maximum number of search results to highlight simultaneously. This limit prevents
76+
* performance degradation when searching for very common terms that would result in excessive
77+
* highlighting decorations.
78+
*/
79+
DEFAULT_HIGHLIGHT_LIMIT = 1000
80+
}
5581

56-
export class SearchAddon extends Disposable implements ITerminalAddon , ISearchApi {
82+
export class SearchAddon extends Disposable implements ITerminalAddon, ISearchApi {
5783
private _terminal: Terminal | undefined;
5884
private _cachedSearchTerm: string | undefined;
5985
private _highlightedLines: Set<number> = new Set();
@@ -72,13 +98,13 @@ export class SearchAddon extends Disposable implements ITerminalAddon , ISearchA
7298
private _linesCacheTimeoutId = 0;
7399
private _linesCacheDisposables = new MutableDisposable();
74100

75-
private readonly _onDidChangeResults = this._register(new Emitter<{ resultIndex: number, resultCount: number }>());
76-
public readonly onDidChangeResults = this._onDidChangeResults.event;
101+
private readonly _onDidChangeResults = this._register(new Emitter<ISearchResultChangeEvent>());
102+
public get onDidChangeResults(): Event<ISearchResultChangeEvent> { return this._onDidChangeResults.event; }
77103

78104
constructor(options?: Partial<ISearchAddonOptions>) {
79105
super();
80106

81-
this._highlightLimit = options?.highlightLimit ?? DEFAULT_HIGHLIGHT_LIMIT;
107+
this._highlightLimit = options?.highlightLimit ?? Constants.DEFAULT_HIGHLIGHT_LIMIT;
82108
}
83109

84110
public activate(terminal: Terminal): void {
@@ -435,7 +461,7 @@ export class SearchAddon extends Disposable implements ITerminalAddon , ISearchA
435461
}
436462

437463
window.clearTimeout(this._linesCacheTimeoutId);
438-
this._linesCacheTimeoutId = window.setTimeout(() => this._destroyLinesCache(), LINES_CACHE_TIME_TO_LIVE);
464+
this._linesCacheTimeoutId = window.setTimeout(() => this._destroyLinesCache(), Constants.LINES_CACHE_TIME_TO_LIVE);
439465
}
440466

441467
private _destroyLinesCache(): void {
@@ -455,8 +481,8 @@ export class SearchAddon extends Disposable implements ITerminalAddon , ISearchA
455481
* @param term the substring that starts at searchIndex
456482
*/
457483
private _isWholeWord(searchIndex: number, line: string, term: string): boolean {
458-
return ((searchIndex === 0) || (NON_WORD_CHARACTERS.includes(line[searchIndex - 1]))) &&
459-
(((searchIndex + term.length) === line.length) || (NON_WORD_CHARACTERS.includes(line[searchIndex + term.length])));
484+
return ((searchIndex === 0) || (Constants.NON_WORD_CHARACTERS.includes(line[searchIndex - 1]))) &&
485+
(((searchIndex + term.length) === line.length) || (Constants.NON_WORD_CHARACTERS.includes(line[searchIndex + term.length])));
460486
}
461487

462488
/**

addons/addon-search/test/SearchAddon.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ test.afterAll(async () => await ctx.page.close());
1919
test.describe('Search Tests', () => {
2020

2121
test.beforeEach(async () => {
22+
await ctx.proxy.reset();
2223
await ctx.page.evaluate(`
23-
window.term.reset()
2424
window.search?.dispose();
2525
window.search = new SearchAddon();
2626
window.term.loadAddon(window.search);

addons/addon-search/typings/addon-search.d.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,21 @@ declare module '@xterm/addon-search' {
7575
activeMatchColorOverviewRuler: string;
7676
}
7777

78+
/**
79+
* Event data fired when search results change.
80+
*/
81+
export interface ISearchResultChangeEvent {
82+
/**
83+
* The index of the currently active result, -1 when the threshold of matches is exceeded.
84+
*/
85+
resultIndex: number;
86+
87+
/**
88+
* The total number of search results found.
89+
*/
90+
resultCount: number;
91+
}
92+
7893
/**
7994
* Options for the search addon.
8095
*/
@@ -139,8 +154,7 @@ declare module '@xterm/addon-search' {
139154
/**
140155
* When decorations are enabled, fires when
141156
* the search results change.
142-
* @returns -1 for resultIndex when the threshold of matches is exceeded.
143157
*/
144-
readonly onDidChangeResults: IEvent<{ resultIndex: number, resultCount: number }>;
158+
readonly onDidChangeResults: IEvent<ISearchResultChangeEvent>;
145159
}
146160
}

src/browser/CoreBrowserTerminal.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -646,6 +646,14 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
646646
if (deltaY === 0) {
647647
return false;
648648
}
649+
const lines = self.coreMouseService.consumeWheelEvent(
650+
ev as WheelEvent,
651+
self._renderService?.dimensions?.device?.cell?.height,
652+
self._coreBrowserService?.dpr
653+
);
654+
if (lines === 0) {
655+
return false;
656+
}
649657
action = deltaY < 0 ? CoreMouseAction.UP : CoreMouseAction.DOWN;
650658
but = CoreMouseButton.WHEEL;
651659
break;
@@ -817,6 +825,15 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
817825
return false;
818826
}
819827

828+
const lines = self.coreMouseService.consumeWheelEvent(
829+
ev as WheelEvent,
830+
self._renderService?.dimensions?.device?.cell?.height,
831+
self._coreBrowserService?.dpr
832+
);
833+
if (lines === 0) {
834+
return false;
835+
}
836+
820837
// Construct and send sequences
821838
const sequence = C0.ESC + (this.coreService.decPrivateModes.applicationCursorKeys ? 'O' : '[') + (ev.deltaY < 0 ? 'A' : 'B');
822839
this.coreService.triggerDataEvent(sequence, true);

src/common/TestUtils.test.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,18 @@ export class MockBufferService implements IBufferService {
2020
public buffers: IBufferSet = {} as any;
2121
public onResize: Event<{ cols: number, rows: number }> = new Emitter<{ cols: number, rows: number }>().event;
2222
public onScroll: Event<number> = new Emitter<number>().event;
23+
private readonly _onScroll = new Emitter<number>();
2324
public isUserScrolling: boolean = false;
2425
constructor(
2526
public cols: number,
2627
public rows: number,
2728
optionsService: IOptionsService = new MockOptionsService()
2829
) {
2930
this.buffers = new BufferSet(optionsService, this);
31+
// Listen to buffer activation events and automatically fire scroll events
32+
this.buffers.onBufferActivate(e => {
33+
this._onScroll.fire(e.activeBuffer.ydisp);
34+
});
3035
}
3136
public scrollPages(pageCount: number): void {
3237
throw new Error('Method not implemented.');
@@ -66,6 +71,9 @@ export class MockCoreMouseService implements ICoreMouseService {
6671
public explainEvents(events: CoreMouseEventType): { [event: string]: boolean } {
6772
throw new Error('Method not implemented.');
6873
}
74+
public consumeWheelEvent(ev: WheelEvent, cellHeight: number, dpr: number): number {
75+
return 1;
76+
}
6977
}
7078

7179
export class MockCharsetService implements ICharsetService {

src/common/services/BufferService.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ export class BufferService extends Disposable implements IBufferService {
3737
this.cols = Math.max(optionsService.rawOptions.cols || 0, MINIMUM_COLS);
3838
this.rows = Math.max(optionsService.rawOptions.rows || 0, MINIMUM_ROWS);
3939
this.buffers = this._register(new BufferSet(optionsService, this));
40+
this._register(this.buffers.onBufferActivate(e => {
41+
this._onScroll.fire(e.activeBuffer.ydisp);
42+
}));
4043
}
4144

4245
public resize(cols: number, rows: number): void {

0 commit comments

Comments
 (0)