Files
orca/config/patches/xterm-src/@xterm__addon-search@0.17.0-beta.300.src.patch
T
Neil 8d5d1d548b fix(terminal): stop terminal search overflowing the stack on long wrapped lines
@xterm/addon-search's SearchEngine._findInLine rewound to the first row of a
wrapped line by calling itself once per wrapped row, so recursion depth equalled
the number of screen rows one logical line occupies. A single un-newlined line
(base64, a minified bundle, one huge log record) in a pane with Orca's scrollback
throws RangeError: Maximum call stack size exceeded out of TerminalSearch's
effect, which safeFind rethrows, tripping the terminal.workbench error boundary
and killing the whole terminal surface (crash report 012eb5be, Orca 1.4.194).

Patch the rewind into a loop, through the existing xterm patch harness: a source
patch under config/patches/xterm-src/ plus the generated dist patch, both emitted
by config/scripts/regenerate-xterm-patches.mjs from the pinned upstream commit.

Still present in @xterm/addon-search 0.17.0-beta.301; worth upstreaming.
2026-09-02 22:42:08 -07:00

36 lines
1.7 KiB
Diff

diff --git a/src/SearchEngine.ts b/src/SearchEngine.ts
index 1760bc2bd1fd274d23e2032fde631b39c739f0d9..f856f1f9444a13d29fcbd2f264b0c2bfbf33db1c 100644
--- a/src/SearchEngine.ts
+++ b/src/SearchEngine.ts
@@ -250,23 +250,19 @@ export class SearchEngine {
* @returns The search result if it was found.
*/
private _findInLine(term: string, searchPosition: ISearchPosition, searchOptions: ISearchOptions = {}, isReverseSearch: boolean = false): ISearchResult | undefined {
- const row = searchPosition.startRow;
- const col = searchPosition.startCol;
-
// Ignore wrapped lines, only consider on unwrapped line (first row of command string).
- const firstLine = this._terminal.buffer.active.getLine(row);
- if (firstLine?.isWrapped) {
+ // This walks back to the line start rather than recursing into it: one frame per wrapped
+ // row overflows the stack on a line long enough to fill the scrollback.
+ while (this._terminal.buffer.active.getLine(searchPosition.startRow)?.isWrapped) {
+ searchPosition.startCol += this._terminal.cols;
if (isReverseSearch) {
- searchPosition.startCol += this._terminal.cols;
return;
}
-
- // This will iterate until we find the line start.
- // When we find it, we will search using the calculated start column.
searchPosition.startRow--;
- searchPosition.startCol += this._terminal.cols;
- return this._findInLine(term, searchPosition, searchOptions);
}
+ const row = searchPosition.startRow;
+ const col = searchPosition.startCol;
+
let cache = this._lineCache.getLineFromCache(row);
if (!cache) {
cache = this._lineCache.translateBufferLineToStringWithWrap(row, true);