74 lines · 2.5 KB
import { diffArrays } from "diff";
import type { BlameRange } from "~/lib/code-intel";

const MAX_EDIT_LENGTH = 20_000;
const DIFF_TIMEOUT_MS = 1000;

/** Maps each new-version line index to its old-version index (-1 when added); null if the diff gave up. */
export function mapLines(oldLines: string[], newLines: string[]): Int32Array | null {
  const changes = diffArrays(oldLines, newLines, { maxEditLength: MAX_EDIT_LENGTH, timeout: DIFF_TIMEOUT_MS });
  if (!changes) return null;
  const map = new Int32Array(newLines.length).fill(-1);
  let oldIndex = 0;
  let newIndex = 0;
  for (const change of changes) {
    const count = change.count;
    if (change.added) newIndex += count;
    else if (change.removed) oldIndex += count;
    else {
      for (let offset = 0; offset < count; offset++) map[newIndex + offset] = oldIndex + offset;
      oldIndex += count;
      newIndex += count;
    }
  }
  return map;
}

export function splitLines(text: string): string[] {
  const lines = text.split("\n");
  if (lines.length > 1 && lines[lines.length - 1] === "") lines.pop();
  return lines;
}

/** Tracks unattributed final-version lines through older versions of a file. */
export class LineTracker {
  readonly owners: Array<string | null>;
  private pending: Array<{ line: number; current: number }>;

  constructor(lineCount: number) {
    this.owners = new Array<string | null>(lineCount).fill(null);
    this.pending = Array.from({ length: lineCount }, (_, line) => ({ line, current: line }));