Z ZPI 文档 v0.3.8
介绍页

源码

packages/ui/src/patch-find.ts

typescript72 行查看原文
import { type FileDiffMetadata, parsePatchFiles } from "@pierre/diffs";
import type { FindTarget } from "./find.ts";

export type PatchFindTarget = FindTarget & { patchLine?: number };

/** Count hunk body lines before interpreting header-like source text. */
function bodyTargets(id: string, path: string, patch: string): PatchFindTarget[] {
  let old = 0,
    current = 0,
    sawHunk = false;
  return patch.split(/\r?\n/).flatMap((line, index) => {
    const hunk = /^@@ -\d+(?:,(\d+))? \+\d+(?:,(\d+))? @@/.exec(line);
    if (hunk) {
      old = Number(hunk[1] ?? 1);
      current = Number(hunk[2] ?? 1);
      sawHunk = true;
      return [];
    }
    const kind = line[0];
    if (!/^[ +-]/.test(line)) return [];
    if (sawHunk ? !old && !current : /^(---|\+\+\+) /.test(line)) return [];
    if (kind !== "+") old = Math.max(0, old - 1);
    if (kind !== "-") current = Math.max(0, current - 1);
    return [{ key: `${id}:plain:${index}`, path, text: line.slice(1), patchLine: index }];
  });
}

/** Use the diff renderer's line indices; headers and diff markers are not searchable text. */
export function patchFindTargets(
  id: string,
  path: string,
  patch: string | FileDiffMetadata,
): PatchFindTarget[] {
  const body = typeof patch === "string" ? bodyTargets(id, path, patch) : undefined;
  try {
    const patches = typeof patch === "string" ? parsePatchFiles(patch, undefined, true) : undefined;
    const diff =
      typeof patch === "string"
        ? patches?.length === 1 && patches[0].files.length === 1
          ? patches[0].files[0]
          : undefined
        : patch;
    if (!diff) return body ?? [];
    const targets: PatchFindTarget[] = [];
    for (const hunk of diff.hunks) {
      let unified = hunk.unifiedLineStart,
        addition = hunk.additionLineIndex,
        deletion = hunk.deletionLineIndex;
      const add = (text: string) => {
        const target: PatchFindTarget = { key: `${id}:${unified++}`, path, text: text.replace(/\r?\n$/, "") };
        if (body) target.patchLine = body[targets.length]?.patchLine;
        targets.push(target);
      };
      for (const content of hunk.hunkContent) {
        if (content.type === "context") {
          for (let i = 0; i < content.lines; i++) add(diff.additionLines[addition++]);
          deletion += content.lines;
        } else {
          for (let i = 0; i < content.deletions; i++) add(diff.deletionLines[deletion++]);
          for (let i = 0; i < content.additions; i++) add(diff.additionLines[addition++]);
        }
      }
    }
    if (!body) return targets;
    if (body.length !== targets.length || body.some((target, index) => target.text !== targets[index].text))
      return body;
    return targets;
  } catch {
    return body ?? [];
  }
}