Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 | 8x 8x 8x 8x 8x 9510x 8x 4755x 4755x 4755x 23807x 14968x 14968x 8839x 8839x 8839x 8839x 15735x 7779x 8839x 8839x 4755x 8x 2366x 2366x 2363x 2363x 2363x 2363x 2363x 43771x 43771x 2363x 2363x 2363x 2351x 2351x 2351x 2351x 2351x 2363x 4755x 4755x 4755x 4755x 4755x 4755x 4755x 30703x 30703x 22747x 4755x 4755x 4755x 4755x 3740x 28666x 28666x 28666x 22045x 22045x 4755x 4755x 3856x 28536x 28536x 28536x 21726x 21726x 8x 33x | import type {LinePatch} from '../line';
import {paint} from './colors';
import {flags} from './flags';
import {hunks} from './hunks';
import {expandLine} from './tabs';
import {
type ColorOptions,
type ContextOptions,
type FlagOptions,
type Hunk,
HUNK_OP_TYPE,
type HunkLine,
type LabelOptions,
NO_NEWLINE,
type TabOptions,
} from './types';
const range = (start: number, count: number): string => (count > 1 ? start + ',' + (start + count - 1) : '' + start);
/**
* Per line, whether its change run holds deletions *and* insertions. GNU marks
* those `!` on both sides and pure runs `-`/`+`, and decides it per run, not per
* hunk: one hunk can carry a `!` run and a `+` run.
*/
const changedRuns = (lines: HunkLine[]): Uint8Array => {
const length = lines.length;
const out = new Uint8Array(length);
for (let i = 0; i < length; ) {
if (lines[i].op === HUNK_OP_TYPE.EQL) {
i++;
continue;
}
let end = i;
let del = false;
let ins = false;
for (; end < length && lines[end].op !== HUNK_OP_TYPE.EQL; end++) {
if (lines[end].op === HUNK_OP_TYPE.DEL) del = true;
else ins = true;
}
if (del && ins) out.fill(1, i, end);
i = end;
}
return out;
};
/**
* Renders hunks as a context diff, `diff -c`. Takes hunks rather than a patch,
* so a `FilePatch` read off the wire renders with `contextHunks(file.hunks, file)`.
*
* @param hunks Hunks to render.
* @param opts File names and timestamps; without a name no `***`/`---` header
* is emitted, since the package reads no filesystem and formats no clocks.
* Also the {@link DiffColors} palette, if any.
* @returns Chunks of the diff, one line each.
*/
export function* contextHunks(
hunks: Hunk[],
opts?: LabelOptions & ColorOptions & TabOptions & FlagOptions,
): Generator<string> {
const length = hunks.length;
if (!length) return;
const colors = opts?.colors;
const reset = colors?.reset ?? '';
const tabs = opts?.tabs;
/** `[written, written before an empty line, re-emitted after a CR]` per marker. */
const MARKS: Record<string, [string, string, string]> = {
' ': flags(' ', opts),
'!': flags('!', opts),
'-': flags('-', opts),
'+': flags('+', opts),
};
const body = (mark: string, text: string): string => {
const [prefix, blank, redraw] = MARKS[mark];
return (text === '' ? blank : prefix) + (tabs ? expandLine(text, tabs, redraw) : text);
};
const oldName = opts?.oldName;
const newName = opts?.newName;
if (oldName !== undefined || newName !== undefined) {
const oldTime = opts?.oldTime;
const newTime = opts?.newTime;
const head = colors?.header;
yield paint(head, '*** ' + (oldName ?? '') + (oldTime === undefined ? '' : '\t' + oldTime), reset) + '\n';
yield paint(head, '--- ' + (newName ?? '') + (newTime === undefined ? '' : '\t' + newTime), reset) + '\n';
}
for (let i = 0; i < length; i++) {
const hunk = hunks[i];
const lines = hunk.lines;
const linesLength = lines.length;
const changed = changedRuns(lines);
let del = false;
let ins = false;
for (let j = 0; j < linesLength; j++) {
const op = lines[j].op;
if (op === HUNK_OP_TYPE.DEL) del = true;
else if (op === HUNK_OP_TYPE.INS) ins = true;
}
const section = hunk.section;
yield '***************' + (section === undefined ? '' : ' ' + section) + '\n';
yield paint(colors?.line, '*** ' + range(hunk.oldStart, hunk.oldCount) + ' ****', reset) + '\n';
if (del)
for (let j = 0; j < linesLength; j++) {
const line = lines[j];
const op = line.op;
if (op === HUNK_OP_TYPE.INS) continue;
yield paint(colors?.del, body(op === HUNK_OP_TYPE.EQL ? ' ' : changed[j] ? '!' : '-', line.text), reset) + '\n';
if (line.noEol) yield NO_NEWLINE + '\n';
}
yield paint(colors?.line, '--- ' + range(hunk.newStart, hunk.newCount) + ' ----', reset) + '\n';
if (ins)
for (let j = 0; j < linesLength; j++) {
const line = lines[j];
const op = line.op;
if (op === HUNK_OP_TYPE.DEL) continue;
yield paint(colors?.add, body(op === HUNK_OP_TYPE.EQL ? ' ' : changed[j] ? '!' : '+', line.text), reset) + '\n';
if (line.noEol) yield NO_NEWLINE + '\n';
}
}
}
/**
* Serializes a line patch as a context diff, `diff -c` / `-C n`, in chunks: no
* diff text is ever assembled into one string, though the hunks are built
* eagerly before the first chunk.
*
* @param src Source lines, without terminators.
* @param dst Destination lines, without terminators.
* @param patch A patch between them, from `lines.diff` or `line.diff`.
* @param opts Context width (`3` by default, `0` is meaningful), labels,
* no-final-newline flags.
* @returns Chunks of the diff, one line each.
*/
export function* context(src: string[], dst: string[], patch: LinePatch, opts?: ContextOptions): Generator<string> {
yield* contextHunks(hunks(src, dst, patch, opts), opts);
}
|