All files / diff/src/format rcs.ts

100% Statements 27/27
100% Branches 10/10
100% Functions 2/2
100% Lines 22/22

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  3x 3x                                         3x 218x 218x 218x 771x 771x 771x 771x   771x 771x 771x 540x 540x 540x 540x 974x 974x 650x                                 3x 215x    
import type {LinePatch} from '../line';
import {hunks} from './hunks';
import {expander} from './tabs';
import {type Hunk, HUNK_OP_TYPE, type RcsOptions, type TabOptions} from './types';
 
/**
 * Renders hunks as an RCS diff, `diff -n`, in file order. Every address is a
 * line of the *original* file, so unlike an ed script this one is not read
 * back-to-front and needs no bottom-up trick.
 *
 * Hunks must carry no context (see {@link rcs}); each renders as `dN count`
 * for its deletions and `aN count` plus the raw lines for its insertions,
 * where the `a` address is the last original line the hunk covers.
 *
 * There is no `\ No newline at end of file` marker in this format and none is
 * needed: the lines are written verbatim, so an unterminated last line is
 * simply written without its terminator.
 *
 * @param hunks Context-free hunks to render, in file order.
 * @param opts The `-t` tab stop, if any. An RCS delta has no line flag, so a
 *     carriage return re-emits nothing.
 * @returns Chunks of the diff, one line each — except a final unterminated line.
 */
export function* rcsHunks(hunks: Hunk[], opts?: TabOptions): Generator<string> {
  const length = hunks.length;
  const text = expander(opts?.tabs);
  for (let i = 0; i < length; i++) {
    const hunk = hunks[i];
    const oldStart = hunk.oldStart;
    const oldCount = hunk.oldCount;
    const newCount = hunk.newCount;
    // Last original line covered; for an empty range that is the line before it.
    const last = oldCount ? oldStart + oldCount - 1 : oldStart;
    if (oldCount) yield 'd' + oldStart + ' ' + oldCount + '\n';
    if (!newCount) continue;
    yield 'a' + last + ' ' + newCount + '\n';
    const lines = hunk.lines;
    const linesLength = lines.length;
    for (let j = 0; j < linesLength; j++) {
      const line = lines[j];
      if (line.op !== HUNK_OP_TYPE.INS) continue;
      yield line.noEol ? text(line.text) : text(line.text) + '\n';
    }
  }
}
 
/**
 * Serializes a line patch as an RCS diff, `diff -n`, in chunks. RCS diffs carry
 * no context and no header, so one change run is one `d`/`a` pair and there is
 * nothing to label.
 *
 * @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 No-final-newline flags; `dstNoEol` drops the terminator from the
 *     last inserted line, which is how this format spells it.
 * @returns Chunks of the diff, one line each — except a final unterminated line.
 */
export function* rcs(src: string[], dst: string[], patch: LinePatch, opts?: RcsOptions): Generator<string> {
  yield* rcsHunks(hunks(src, dst, patch, {...opts, context: 0}), opts);
}