Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
bd79ccc
Move "data" from BufferLine to new LogicalLine class.
PerBothner Apr 7, 2026
45d9f7a
Merge branch 'master' into LogicalLine
PerBothner May 4, 2026
ab5d344
Merge remote-tracking branch 'upstream/master' into LogicalLine
PerBothner Jun 7, 2026
b469616
Un-remove whitespace to match upsteam.
PerBothner Jun 7, 2026
99250d1
WIP - first sketch for using _chars array for combined chars.
PerBothner Aug 13, 2026
d41cc7d
Merge branch 'master' into LogicalLine
PerBothner Aug 14, 2026
b1edb57
Optimize _resizeData to need fewer (re-)allocations
PerBothner Aug 16, 2026
69bb5a2
Merge remote-tracking branch 'upstream/master' into LogicalLine
PerBothner Aug 22, 2026
ce06560
New LineBuffer.setCellsFromCodepoints
PerBothner Aug 24, 2026
7a3fa48
Allocate sucessive lines in normal buffer from large block.
PerBothner Aug 28, 2026
d86580c
Merge remote-tracking branch 'upstream/master' into LogicalLine
PerBothner Aug 28, 2026
fbc57f6
Minor optimization - don't check for url links on each character
PerBothner Aug 29, 2026
3ce563c
Use recycled lines from CircularList trimming
PerBothner Aug 29, 2026
1493c24
Merge remote-tracking branch 'upstream/master' into LogicalLine
PerBothner Aug 31, 2026
1ccc47e
Use new COLOR_MASK constant when setting LogicalLine's backgroundColor.
PerBothner Sep 1, 2026
a3be9ce
Fix missing addition of LogicalLine._dataStart in setCellFromCodepoint.
PerBothner Sep 1, 2026
b2a36f5
Change getExtended to return undefined when there is no ExtendedAttrs
PerBothner Sep 1, 2026
e53f2d3
Fix issue in setCellFromCodepoint reported by lint
PerBothner Sep 1, 2026
6ce5d6b
Fixes to integration tests.
PerBothner Sep 1, 2026
55f5c2d
Merge branch 'LogicalLine' into LineChars
PerBothner Sep 3, 2026
529f34c
Fix a bunch of testsuite failures.
PerBothner Sep 7, 2026
de8c655
Fix some testsuite failures.
PerBothner Sep 22, 2026
1ed1c03
Change CellData handling of well without codepoint.
PerBothner Sep 22, 2026
5994e06
Fixes relating to surrogates, combined chars, wide chars.
PerBothner Sep 24, 2026
06fb650
Remove trimRight argument from translateBufferLineToStringWithWrap in…
PerBothner Sep 24, 2026
5af9ecd
Remove needless recursion in SearchEngine.ts
PerBothner Sep 24, 2026
910e235
Remove parameter comment for removed trimRight parameter.
PerBothner Sep 24, 2026
395bf7f
Move BufferColumn and LogicalColumn to Types.ts.
PerBothner Sep 25, 2026
f707e8e
Rename LogicalLine's getTrimmedString to asString.
PerBothner Sep 25, 2026
3967a93
Speed up addon-search, especifically _stringLengthToBufferSize.
PerBothner Sep 26, 2026
3747646
Speed up BufferApiView.getLine and remove BufferLineApiView
PerBothner Sep 26, 2026
8f20617
Update serialize test since BufferLineApiView class is now gone
PerBothner Sep 27, 2026
7cfa575
Fix bug in CellData function getChars.
PerBothner Sep 27, 2026
c597d62
Add forEachBufferLine method to LogicalLine.
PerBothner Sep 27, 2026
8e5113b
Remove SearchLineCache from addon-search.
PerBothner Sep 27, 2026
f7f78ee
Fix bogus zero-width calls to createCellData.
PerBothner Sep 29, 2026
c1b9825
Fix insertCells handling of final wide char
PerBothner Sep 29, 2026
c58aad9
Optimization to _reflowLarger and remove BufferReflow.ts.
PerBothner Sep 29, 2026
bed9e81
Change isNullChar to check if previous is wide.
PerBothner Sep 30, 2026
02f1239
Update tests for trabslateToString caching.
PerBothner Sep 30, 2026
21686e1
Remove old commented-out code.
PerBothner Oct 2, 2026
2a91169
Add some tests for LogicalLine.asString and BufferLine.startColumn.
PerBothner Oct 2, 2026
f073c38
Move misleadingly-named DEFAULT-ATTR to TestUtils.test.ts
PerBothner Oct 2, 2026
00af54a
Tweaks to DomRendererRowFactory.test.ts for null handling
PerBothner Oct 2, 2026
bcb7cc8
Tweaks to addCodepointToCell
PerBothner Oct 5, 2026
b5c2000
Fix translateToStrng if start column > validEnd
PerBothner Oct 7, 2026
7c8f52e
Fix some lint complaints
PerBothner Oct 7, 2026
bf0c908
Rename LogicalLine length property to trimmedLength.
PerBothner Oct 7, 2026
38e5b68
Update serialize test for length -> trimmedLength rename.
PerBothner Oct 7, 2026
2a89ceb
For LogicalLine._data use Uint32Array byte-offsets
PerBothner Oct 9, 2026
7d65a11
Merge remote-tracking branch 'upstream/master' into LineChars
PerBothner Oct 14, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions addons/addon-image/test/ImageAddon.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,8 @@ test.describe('ImageAddon', () => {
);
}
// bufferline privates
strictEqual(await ctx.page.evaluate('window.term._core.buffer.lines.get(0)._data instanceof Uint32Array'), true);
strictEqual(await ctx.page.evaluate('window.term._core.buffer.lines.get(0)._extendedAttrs instanceof Object'), true);
strictEqual(await ctx.page.evaluate('window.term._core.buffer.lines.get(0)._logicalLine._data instanceof Uint32Array'), true);
strictEqual(await ctx.page.evaluate('window.term._core.buffer.lines.get(0)._logicalLine._extendedAttrs instanceof Object'), true);
// inputhandler privates
strictEqual(await ctx.page.evaluate('window.term._core._inputHandler._curAttrData.constructor.name'), '_AttributeData');
strictEqual(await ctx.page.evaluate('window.term._core._inputHandler._parser.constructor.name'), 'EscapeSequenceParser');
Expand Down
3 changes: 1 addition & 2 deletions addons/addon-search/src/DecorationManager.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import { assert } from 'chai';
import { DecorationManager } from './DecorationManager';
import { SearchEngine } from './SearchEngine';
import { SearchLineCache } from './SearchLineCache';
import { Terminal } from 'browser/public/Terminal';
import type { ISearchDecorationOptions } from '@xterm/addon-search';
import type { IDecorationOptions } from '@xterm/xterm';
Expand All @@ -29,7 +28,7 @@ describe('DecorationManager', () => {

it('should split highlight decorations for a wrapped match', async () => {
await writeP(terminal, '0123456789abcde');
const searchEngine = new SearchEngine(terminal, store.add(new SearchLineCache(terminal)));
const searchEngine = new SearchEngine(terminal);
const match = searchEngine.find('9abc', 0, 0);
assert.ok(match);

Expand Down
5 changes: 1 addition & 4 deletions addons/addon-search/src/SearchAddon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import type { SearchAddon as ISearchApi, ISearchOptions, ISearchAddonOptions, IS
import { Emitter, type IEvent } from 'common/Event';
import { Disposable, MutableDisposable, toDisposable } from 'common/Lifecycle';
import { disposableTimeout } from 'common/Async';
import { SearchLineCache } from './SearchLineCache';
import { SearchState } from './SearchState';
import { SearchEngine, type ISearchResult } from './SearchEngine';
import { DecorationManager } from './DecorationManager';
Expand All @@ -34,7 +33,6 @@ export class SearchAddon extends Disposable implements ITerminalAddon, ISearchAp
private _terminal: Terminal | undefined;
private _highlightLimit: number;
private _highlightTimeout = this._register(new MutableDisposable<IDisposable>());
private _lineCache = this._register(new MutableDisposable<SearchLineCache>());

// Component instances
private _state = new SearchState();
Expand All @@ -59,8 +57,7 @@ export class SearchAddon extends Disposable implements ITerminalAddon, ISearchAp

public activate(terminal: Terminal): void {
this._terminal = terminal;
this._lineCache.value = new SearchLineCache(terminal);
this._engine = new SearchEngine(terminal, this._lineCache.value);
this._engine = new SearchEngine(terminal);
this._decorationManager = new DecorationManager(terminal);
this._register(this._terminal.onWriteParsed(() => this._updateMatches()));
this._register(this._terminal.onResize(() => this._updateMatches()));
Expand Down
9 changes: 1 addition & 8 deletions addons/addon-search/src/SearchEngine.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
*/
import { assert } from 'chai';
import { SearchEngine } from './SearchEngine';
import { SearchLineCache } from './SearchLineCache';
import { Terminal } from 'browser/public/Terminal';
import type { ISearchOptions } from '@xterm/addon-search';
import { DisposableStore } from 'common/Lifecycle';
Expand All @@ -16,14 +15,12 @@ function writeP(terminal: Terminal, data: string): Promise<void> {
describe('SearchEngine', () => {
let store: DisposableStore;
let terminal: Terminal;
let lineCache: SearchLineCache;
let searchEngine: SearchEngine;

beforeEach(() => {
store = new DisposableStore();
terminal = store.add(new Terminal({ cols: 80, rows: 24 }));
lineCache = store.add(new SearchLineCache(terminal));
searchEngine = new SearchEngine(terminal, lineCache);
searchEngine = new SearchEngine(terminal);
});

afterEach(() => {
Expand Down Expand Up @@ -663,9 +660,6 @@ describe('SearchEngine', () => {
it('should use cache for line translation', async () => {
await writeP(terminal, 'Hello World');

// Initialize cache
lineCache.initLinesCache();

const result1 = searchEngine.find('World', 0, 0);
const result2 = searchEngine.find('World', 0, 0);

Expand All @@ -688,7 +682,6 @@ describe('SearchEngine', () => {

it('should work correctly with cache invalidation', async () => {
await writeP(terminal, 'Initial text');
lineCache.initLinesCache();

const result1 = searchEngine.find('Initial', 0, 0);
assert.notStrictEqual(result1, undefined);
Expand Down
81 changes: 30 additions & 51 deletions addons/addon-search/src/SearchEngine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@

import type { Terminal } from '@xterm/xterm';
import type { ISearchOptions } from '@xterm/addon-search';
import type { SearchLineCache } from './SearchLineCache';
import { CellData } from 'common/buffer/CellData';
import type { ITerminal } from 'browser/Types';

/**
* Represents the position to start a search from.
Expand Down Expand Up @@ -43,9 +44,11 @@ const enum Constants {
*/
export class SearchEngine {
constructor(
private readonly _terminal: Terminal,
private readonly _lineCache: SearchLineCache
) {}
private readonly _terminal: Terminal
) {
this._core = (this._terminal as any)._core;
}
private _core: ITerminal;

/**
* Find the first occurrence of a term starting from a specific position.
Expand All @@ -64,8 +67,6 @@ export class SearchEngine {
throw new Error(`Invalid col: ${startCol} to search in terminal of ${this._terminal.cols} cols`);
}

this._lineCache.initLinesCache();

const searchPosition: ISearchPosition = {
startRow,
startCol
Expand Down Expand Up @@ -115,8 +116,6 @@ export class SearchEngine {
}
}

this._lineCache.initLinesCache();

const searchPosition: ISearchPosition = {
startRow,
startCol
Expand Down Expand Up @@ -177,7 +176,6 @@ export class SearchEngine {
const startCol = this._terminal.cols;
const isReverseSearch = true;

this._lineCache.initLinesCache();
const searchPosition: ISearchPosition = {
startRow,
startCol
Expand Down Expand Up @@ -250,11 +248,11 @@ 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;
let row = searchPosition.startRow;
let col = searchPosition.startCol;

// Ignore wrapped lines, only consider on unwrapped line (first row of command string).
const firstLine = this._terminal.buffer.active.getLine(row);
let firstLine = this._core.buffer.lines.get(row);
if (firstLine?.isWrapped) {
if (isReverseSearch) {
searchPosition.startCol += this._terminal.cols;
Expand All @@ -263,16 +261,16 @@ export class SearchEngine {

// 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);
}
let cache = this._lineCache.getLineFromCache(row);
if (!cache) {
cache = this._lineCache.translateBufferLineToStringWithWrap(row, true);
this._lineCache.setLineInCache(row, cache);
do {
row--;
col += this._terminal.cols;
firstLine = this._core.buffer.lines.get(row);
} while (firstLine?.isWrapped);
searchPosition.startRow = row;
searchPosition.startCol = col;
}
const [stringLine, offsets] = cache;
const logical = firstLine?.logical();
const stringLine = logical ? logical.asString() : '';

const offset = this._bufferColsToStringOffset(row, col);
let searchTerm = term;
Expand Down Expand Up @@ -314,6 +312,10 @@ export class SearchEngine {
if (searchOptions.wholeWord && !this._isWholeWord(resultIndex, searchStringLine, term)) {
return;
}
const offsets: number[] = [];
logical?.forEachBufferLine((line) => {
offsets.push(logical.offsetInString(line.startColumn));
});

// Adjust the row number and search index if needed since a "line" of text can span multiple
// rows
Expand Down Expand Up @@ -341,12 +343,13 @@ export class SearchEngine {
}

private _stringLengthToBufferSize(row: number, offset: number): number {
const line = this._terminal.buffer.active.getLine(row);
const line = this._core.buffer.lines.get(row);
if (!line) {
return 0;
}
const cell = new CellData();
for (let i = 0; i < offset; i++) {
const cell = line.getCell(i);
line.loadCell(i, cell);
if (!cell) {
break;
}
Expand All @@ -355,38 +358,14 @@ export class SearchEngine {
if (char.length > 1) {
offset -= char.length - 1;
}
// Adjust the searchIndex for empty characters following wide unicode
// chars (eg. CJK)
const nextCell = line.getCell(i + 1);
if (nextCell && nextCell.getWidth() === 0) {
offset++;
}
const width = cell.getWidth();
if (width > 1) { offset += width - 1; }
}
return offset;
}

private _bufferColsToStringOffset(startRow: number, cols: number): number {
let lineIndex = startRow;
let offset = 0;
let line = this._terminal.buffer.active.getLine(lineIndex);
while (cols > 0 && line) {
for (let i = 0; i < cols && i < this._terminal.cols; i++) {
const cell = line.getCell(i);
if (!cell) {
break;
}
if (cell.getWidth()) {
// Treat null characters as whitespace to align with the translateToString API
offset += cell.getCode() === 0 ? 1 : cell.getChars().length;
}
}
lineIndex++;
line = this._terminal.buffer.active.getLine(lineIndex);
if (line && !line.isWrapped) {
break;
}
cols -= this._terminal.cols;
}
return offset;
const line = this._core.buffer.lines.get(startRow)!;
return line ? line.logical().offsetInString(cols) : 0;
}
}
Loading
Loading