Z ZPI 文档 v0.3.8
介绍页

源码

packages/ui/src/review-find.ts

typescript110 行查看原文
import { getSingularPatch, parseDiffFromFile } from "@pierre/diffs";
import type { FindTarget } from "./find.ts";
import { patchFindTargets } from "./patch-find.ts";
import { getPatchPreviewLineContent, parseTruncatedMarkerOmittedLineCount } from "./patch-preview.ts";
import { getGitPaneDiffPreviewPlan, type ReviewDiffData } from "./review-diff.ts";

export function reviewFindTargets(id: string, diff: ReviewDiffData): FindTarget[] {
  if (diff.availability !== "patch") return [];
  const plan = getGitPaneDiffPreviewPlan(diff);
  const plain =
    plan.kind === "plain-text"
      ? plan.lines.flatMap((line, index) =>
          parseTruncatedMarkerOmittedLineCount(line) !== null
            ? []
            : [
                {
                  key: `${id}:plain:${index}`,
                  path: diff.path,
                  text: getPatchPreviewLineContent(line),
                },
              ],
        )
      : [];
  if (diff.beforeContent === null || diff.afterContent === null)
    return plan.kind === "plain-text" ? plain : patchFindTargets(id, diff.path, diff.patch ?? "");
  if (plan.kind !== "rich") {
    const keys = [new Map<number, string>(), new Map<number, string>()];
    try {
      const patch = getSingularPatch(diff.patch ?? "");
      const visible = plan.kind === "plain-text" ? plain : patchFindTargets(id, diff.path, patch);
      let index = 0;
      const key = (line: string) => {
        const target = visible[index++];
        return target?.text === line.replace(/\r?\n$/, "") ? target.key : undefined;
      };
      for (const hunk of patch.hunks) {
        let old = hunk.deletionLineIndex,
          next = hunk.additionLineIndex;
        const assign = (side: number, line: number, value: string | undefined) => {
          if (value) keys[side].set(line, value);
        };
        for (const content of hunk.hunkContent) {
          if (content.type === "context") {
            for (let i = 0; i < content.lines; i++) {
              const value = key(patch.additionLines[next]);
              assign(0, old++, value);
              assign(1, next++, value);
            }
          } else {
            for (let i = 0; i < content.deletions; i++) assign(0, old, key(patch.deletionLines[old++]));
            for (let i = 0; i < content.additions; i++) assign(1, next, key(patch.additionLines[next++]));
          }
        }
      }
    } catch {
      // Metadata-only or malformed fallbacks have no reliable patch coordinates.
    }
    return [diff.beforeContent, diff.afterContent].flatMap((content, side) => {
      const lines = content.split(/\r?\n/);
      if (content.endsWith("\n")) lines.pop();
      return lines.map((text, index) => ({
        key: keys[side].get(index) ?? `${id}:hidden:${side}:${index}`,
        path: diff.path,
        text,
      }));
    });
  }
  const parsed = parseDiffFromFile(
    { name: diff.path, contents: diff.beforeContent },
    { name: diff.path, contents: diff.afterContent },
  );
  const before: FindTarget[] = [],
    after: FindTarget[] = [];
  let old = 0,
    current = 0,
    unified = 0;
  const target = (text: string): FindTarget => ({
    key: `${id}:${unified}`,
    path: diff.path,
    text: text.replace(/\r?\n$/, ""),
  });
  const context = () => {
    const line = target(parsed.additionLines[current++]);
    before.push(line);
    after.push(line);
    old++;
    unified++;
  };
  for (const hunk of parsed.hunks) {
    while (current < hunk.additionLineIndex) context();
    for (const content of hunk.hunkContent) {
      if (content.type === "context") {
        for (let i = 0; i < content.lines; i++) context();
      } else {
        for (let i = 0; i < content.deletions; i++) {
          before.push(target(parsed.deletionLines[old++]));
          unified++;
        }
        for (let i = 0; i < content.additions; i++) {
          after.push(target(parsed.additionLines[current++]));
          unified++;
        }
      }
    }
  }
  while (current < parsed.additionLines.length) context();
  // ZCode searches before, then after. Unchanged text counts once per snapshot even in unified view.
  return [...before, ...after];
}