/** * Line-level diffing for the tool card, and the word-level emphasis inside a * changed pair. * * The card used to print every old line as `-` and every new line as `+`: a * one-line change in a hundred-line file produced two hundred rows, which is * unreadable and hides the change it is trying to show. This module keeps the * classic shape — common lines as context, changes as `-`/`+` pairs, unchanged * stretches collapsed to a marker — and reports which characters inside a * replaced pair actually changed so the renderer can emphasise them. * @module dsh-ssh-tui/line-diff */ /** One line of the diff. */ export interface DiffLine { kind: 'same' | 'add' | 'del'; text: string; /** 0-based index in the old text, for `same` and `del`. */ oldIndex?: number; /** 0-based index in the new text, for `same` and `add`. */ newIndex?: number; } /** A run of lines with its context, and how many unchanged lines were skipped. */ export interface DiffHunk { /** Unchanged lines collapsed before this run; 0 for the first. */ omittedBefore: number; lines: DiffLine[]; } /** Two columns plus a gutter need this much room to be worth it. */ export declare const SIDE_BY_SIDE_MIN_WIDTH = 100; /** The gutter between the two columns. */ export declare const SIDE_BY_SIDE_GUTTER = " \u2502 "; /** * Whether a diff should be shown side by side at this terminal width. * * Below the threshold the columns would be narrower than the code they hold, * which turns every changed line into a wrapped mess — stacked reads better. * @param width - the terminal width in cells. * @returns the layout to use. */ export declare function diffLayout(width: number): 'side-by-side' | 'stacked'; /** One row of a side-by-side rendering. */ export interface SideBySideRow { /** Marker plus text for the left column: `- old`, ` context`, or empty. */ left: string; /** Marker plus text for the right column: `+ new`, ` context`, or empty. */ right: string; /** Emphasis inside `left`, offsets into it. */ leftSpans: DiffSpan[]; /** Emphasis inside `right`, offsets into it. */ rightSpans: DiffSpan[]; /** Whether both columns carry the same line (context) rather than a change. */ context: boolean; } /** * Pair a line diff into two columns. * * A removed line immediately followed by an added one is a replacement and * shares a row, with the changed characters emphasised on each side; a lone * removal or addition keeps its own row with the other column empty. Context * rows carry the text on both sides so the eye can follow either column. * @param lines - a diff, as {@link diffLines} returns it. * @returns the rows, in order. */ export declare function sideBySideRows(lines: readonly DiffLine[]): SideBySideRow[]; /** One emphasized range inside a line, in UTF-16 offsets. */ export interface DiffSpan { start: number; end: number; } /** Split text into lines, treating an empty string as no lines. */ export declare function splitLines(text: string): string[]; /** * Diff two texts line by line. * * Equal lines are `same`, lines only in the old text are `del`, only in the new * are `add` — in the order a reader expects. A pair this module cannot afford * (see {@link MAX_DIFF_LINES}) degrades to "all removed, then all added", which * is what the card rendered before but is at least bounded. * @param oldText - the previous content; empty for a new file. * @param newText - the new content. * @returns the diff, oldest first. */ export declare function diffLines(oldText: string, newText: string): DiffLine[]; /** * Group a diff into runs of change with `context` unchanged lines around them, * reporting how many unchanged lines were left out between runs. * @param lines - a diff, as {@link diffLines} returns it. * @param context - unchanged lines to keep on each side of a change. * @returns the runs, in order; a diff with no changes yields no runs. */ export declare function diffHunks(lines: readonly DiffLine[], context?: number): DiffHunk[]; /** * The characters that differ between two lines: the common prefix and suffix * are trimmed away, and what is left is emphasised. * * Whole-line emphasis was the old behaviour and it reads as noise on a line that * changed one identifier. Trimming is greedy and therefore approximate — it does * not try to align moved words — but it is exact about the boundaries it does * report: everything outside the spans really is identical. * @param oldText - the removed line. * @param newText - the added line. * @returns one span list per side, possibly empty when they are equal. */ export declare function wordDiffSpans(oldText: string, newText: string): { old: DiffSpan[]; new: DiffSpan[]; };