Files
Triple-C/app/src/lib/xtermLineJoin.ts
T

66 lines
2.3 KiB
TypeScript
Raw Normal View History

/**
* Joins an xterm buffer row with its wrapped continuations.
*
* `WebLinksAddon` does the same in its private `LinkComputer`, which the
* built package does not export — so the walk is repeated here, with the same
* 2048-character budget. Rows are read with `translateToString(true)`, which
* trims the right edge; a wrap never ends in trailing spaces xterm would keep,
* so the join is exact for the text a path can occur in.
*
* Wide characters (CJK, emoji) occupy two cells but one string index, so a
* column computed from a string offset drifts right of the glyph on such rows.
* The addon corrects this with `getCell`; v1 accepts the drift (underline
* lands a cell early; the click still resolves the same link).
*/
export const MAX_JOINED_LENGTH = 2048;
/** Minimal slice of xterm's IBuffer this needs. */
export interface RowSource {
getLine(y: number): { isWrapped: boolean; translateToString(trimRight?: boolean): string } | undefined;
}
export interface JoinedLine {
text: string;
/** 0-based index of the first buffer row that contributed. */
firstRow: number;
/** For each contributed row (in order), the string offset at which it starts. */
rowStarts: number[];
}
export function joinWrappedRows(buffer: RowSource, row: number): JoinedLine {
let top = row;
while (top > 0 && buffer.getLine(top)?.isWrapped) top--;
const parts: string[] = [];
let length = 0;
let y = top;
for (;;) {
const line = buffer.getLine(y);
if (!line) break;
if (y !== top && !line.isWrapped) break;
const text = line.translateToString(true);
if (length + text.length > MAX_JOINED_LENGTH && parts.length > 0) break;
parts.push(text);
length += text.length;
y++;
}
const rowStarts: number[] = [];
let offset = 0;
for (const p of parts) {
rowStarts.push(offset);
offset += p.length;
}
return { text: parts.join(""), firstRow: top, rowStarts };
}
/** String offset → 1-based {x, y} cell (y is the buffer row + 1). */
export function offsetToCell(joined: JoinedLine, offset: number): { x: number; y: number } {
let rowIdx = 0;
for (let i = 0; i < joined.rowStarts.length; i++) {
if (joined.rowStarts[i] <= offset) rowIdx = i;
}
return { x: offset - joined.rowStarts[rowIdx] + 1, y: joined.firstRow + rowIdx + 1 };
}