Initial open-source release

Claude-Session: https://claude.ai/code/session_01B65qLEKnkeYTGdbwhLpjCu
Цей коміт міститься в:
l0ng-ai committed 2026-10-22 18:55:10 +08:00
коміт 9a2025b76e
2034 files changed
+243381

No files matched your search

+158
Переглянути файл
@@ -0,0 +1,158 @@
/** Diff editor inputs and helpers to open them. */
import type { ChangedFile } from "@/bindings";
import { call, commands } from "@/platform/ipc";
import { editorService, type EditorInput } from "@/workbench/contributions";
import { baseName, effectiveKind } from "./model";
import { readWorkingText } from "./ops";
import { statusOf, useVcs } from "./store";
export type DiffSide =
/** File content at a git revision (`HEAD`, `:0` = index, a commit id, `<id>^`). */
| { type: "rev"; rev: string; path?: string; label?: string }
/** The working file (unsaved editor buffer included). */
| { type: "worktree"; label?: string }
/** Another file (Compare With…, Compare Files); editable like the working file. */
| { type: "file"; path: string; label?: string }
| { type: "text"; text: string; label: string }
/** A Local History revision. */
| { type: "history"; id: string; label: string };
/** A note attached below a line of one side (pull request review comments). */
export interface DiffAnnotation {
side: "left" | "right";
/** 0-based line of that side. */
line: number;
author: string;
body: string;
/** ISO date. */
date?: string;
}
export interface DiffData {
/** Absolute path of the file (the right side's path). */
path: string;
left: DiffSide;
right: DiffSide;
/** Reveal this 0-based line of the right side. */
line?: number;
/** Tab title instead of the default "name (left ↔ right)". */
title?: string;
/** Notes shown under lines (unified mode shows the right side's only). */
annotations?: DiffAnnotation[];
}
export const DIFF_KIND = "diff";
export function sideKey(s: DiffSide): string {
switch (s.type) {
case "rev":
return `rev:${s.rev}:${s.path ?? ""}`;
case "worktree":
return "wt";
case "file":
return `file:${s.path}`;
case "text":
return `text:${s.label}:${textHash(s.text)}`;
case "history":
return `hist:${s.id}`;
}
}
/** Short content hash so different texts under one label get their own tab. */
function textHash(text: string): string {
let h = 0x811c9dc5;
for (let i = 0; i < text.length; i++) h = Math.imul(h ^ text.charCodeAt(i), 0x01000193);
return (h >>> 0).toString(36);
}
export function sideLabel(s: DiffSide): string {
switch (s.type) {
case "rev":
if (s.label) return s.label;
if (s.rev === "HEAD") return "HEAD";
if (s.rev === ":0" || s.rev === ":") return "Staged (index)";
return s.rev.length >= 40 ? s.rev.slice(0, 8) : s.rev;
case "worktree":
return s.label ?? "Your version";
case "file":
return s.label ?? s.path;
case "text":
case "history":
return s.label;
}
}
export function diffInput(data: DiffData): EditorInput {
return { kind: DIFF_KIND, key: `diff:${data.path}:${sideKey(data.left)}:${sideKey(data.right)}`, path: data.path, data };
}
export function diffTitle(data: DiffData): string {
if (data.title) return data.title;
if (data.left.type === "file") return `${baseName(data.left.path)} ↔ ${baseName(data.path)}`;
return `${baseName(data.path)} (${sideLabel(data.left)} ↔ ${sideLabel(data.right)})`;
}
export async function openDiff(data: DiffData, opts: { preview?: boolean } = {}) {
await editorService().openInput(diffInput(data), { preview: opts.preview, focus: true });
}
/** Default local-changes diff for a working file: HEAD (or empty) vs the working copy. */
export function workingDiffData(path: string, line?: number): DiffData {
const st = statusOf(path);
const kind = st ? effectiveKind(st) : null;
const left: DiffSide =
kind === "untracked" || kind === "added"
? { type: "text", text: "", label: "(does not exist)" }
: st?.renamedFrom
? { type: "rev", rev: "HEAD", path: joinRoot(st.renamedFrom), label: `HEAD (${st.renamedFrom})` }
: { type: "rev", rev: "HEAD" };
const right: DiffSide =
kind === "deleted" ? { type: "text", text: "", label: "(deleted)" } : { type: "worktree" };
return { path, left, right, line };
}
function joinRoot(rel: string): string {
const root = useVcs.getState().root;
return root ? `${root}/${rel}` : rel;
}
/** Diff of one file changed by `commit` (first parent ↔ commit). */
export function commitFileDiffData(commit: { id: string; shortId: string; parents: string[] }, file: ChangedFile): DiffData {
const path = joinRoot(file.path);
const parent = commit.parents[0];
const left: DiffSide =
!parent || file.kind === "added"
? { type: "text", text: "", label: "(does not exist)" }
: {
type: "rev",
rev: parent,
path: file.oldPath ? joinRoot(file.oldPath) : undefined,
label: parent.slice(0, 8),
};
const right: DiffSide =
file.kind === "deleted"
? { type: "text", text: "", label: "(deleted)" }
: { type: "rev", rev: commit.id, label: commit.shortId };
return { path, left, right };
}
export async function loadSide(path: string, side: DiffSide): Promise<string> {
switch (side.type) {
case "rev":
return (await call(commands.vcsFileAt(side.path ?? path, side.rev))) ?? "";
case "worktree":
return (await readWorkingText(path)) ?? "";
case "file":
return (await readWorkingText(side.path)) ?? "";
case "text":
return side.text;
case "history":
return call(commands.historyContent(side.id));
}
}
export const isWorktree = (s: DiffSide) => s.type === "worktree";
/** Path of a side backed by a file on disk (editable in the diff), else null. */
export const sideFile = (s: DiffSide, path: string): string | null =>
s.type === "worktree" ? path : s.type === "file" ? s.path : null;
export const isIndex = (s: DiffSide) => s.type === "rev" && (s.rev === ":0" || s.rev === ":");