118 lines
3.2 KiB
TypeScript
118 lines
3.2 KiB
TypeScript
/**
|
|
* ANSI-aware text measurement and slicing.
|
|
*
|
|
* High-performance UTF-16 native implementation with ASCII fast-paths,
|
|
* single-pass ANSI scanning, and proper Unicode grapheme cluster support.
|
|
*/
|
|
|
|
import { native } from "../native.js";
|
|
import type { ExtractSegmentsResult, SliceResult } from "./types.js";
|
|
|
|
export { EllipsisKind } from "./types.js";
|
|
export type { ExtractSegmentsResult, SliceResult };
|
|
|
|
/**
|
|
* Word-wrap text to a visible width, preserving ANSI escape codes across
|
|
* line breaks.
|
|
*
|
|
* Active SGR codes (colors, bold, etc.) are carried to continuation lines.
|
|
* Underline and strikethrough are reset at line ends and restored on the
|
|
* next line.
|
|
*/
|
|
export function wrapTextWithAnsi(
|
|
text: string,
|
|
width: number,
|
|
tabWidth?: number,
|
|
): string[] {
|
|
return (
|
|
native as Record<string, (...args: unknown[]) => unknown>
|
|
).wrapTextWithAnsi(text, width, tabWidth) as string[];
|
|
}
|
|
|
|
/**
|
|
* Truncate text to a visible width with an optional ellipsis.
|
|
*
|
|
* @param text Input string (may contain ANSI codes).
|
|
* @param maxWidth Maximum visible width in terminal cells.
|
|
* @param ellipsisKind 0 = "\u2026", 1 = "...", 2 = none.
|
|
* @param pad When true, pad with spaces to exactly `maxWidth`.
|
|
* @param tabWidth Tab stop width (default 3, range 1-16).
|
|
*/
|
|
export function truncateToWidth(
|
|
text: string,
|
|
maxWidth: number,
|
|
ellipsisKind: number,
|
|
pad: boolean,
|
|
tabWidth?: number,
|
|
): string {
|
|
return (
|
|
native as Record<string, (...args: unknown[]) => unknown>
|
|
).truncateToWidth(text, maxWidth, ellipsisKind, pad, tabWidth) as string;
|
|
}
|
|
|
|
/**
|
|
* Slice a range of visible columns from a line.
|
|
*
|
|
* Counts terminal cells (skipping ANSI escapes). When `strict` is true,
|
|
* wide characters that would exceed the range are excluded.
|
|
*/
|
|
export function sliceWithWidth(
|
|
line: string,
|
|
startCol: number,
|
|
length: number,
|
|
strict: boolean,
|
|
tabWidth?: number,
|
|
): SliceResult {
|
|
return (
|
|
native as Record<string, (...args: unknown[]) => unknown>
|
|
).sliceWithWidth(line, startCol, length, strict, tabWidth) as SliceResult;
|
|
}
|
|
|
|
/**
|
|
* Extract the before/after segments around an overlay region.
|
|
*
|
|
* ANSI state is tracked so the `after` segment renders correctly even when
|
|
* the overlay truncates styled text.
|
|
*/
|
|
export function extractSegments(
|
|
line: string,
|
|
beforeEnd: number,
|
|
afterStart: number,
|
|
afterLen: number,
|
|
strictAfter: boolean,
|
|
tabWidth?: number,
|
|
): ExtractSegmentsResult {
|
|
return (
|
|
native as Record<string, (...args: unknown[]) => unknown>
|
|
).extractSegments(
|
|
line,
|
|
beforeEnd,
|
|
afterStart,
|
|
afterLen,
|
|
strictAfter,
|
|
tabWidth,
|
|
) as ExtractSegmentsResult;
|
|
}
|
|
|
|
/**
|
|
* Strip ANSI escape sequences, remove control characters and lone
|
|
* surrogates, and normalize line endings (CR removed).
|
|
*
|
|
* Returns the original string when no changes are needed (zero-copy).
|
|
*/
|
|
export function sanitizeText(text: string): string {
|
|
return (
|
|
native as Record<string, (...args: unknown[]) => unknown>
|
|
).sanitizeText(text) as string;
|
|
}
|
|
|
|
/**
|
|
* Calculate visible width of text excluding ANSI escape sequences.
|
|
*
|
|
* Tabs count as `tabWidth` cells (default 3).
|
|
*/
|
|
export function visibleWidth(text: string, tabWidth?: number): number {
|
|
return (
|
|
native as Record<string, (...args: unknown[]) => unknown>
|
|
).visibleWidth(text, tabWidth) as number;
|
|
}
|