дзеркало
https://github.com/l0ng-ai/openidea.git
синхронізовано 2026-11-09 19:25:54 +02:00
Initial open-source release
Claude-Session: https://claude.ai/code/session_01B65qLEKnkeYTGdbwhLpjCu
Цей коміт міститься в:
коміт
9a2025b76e
2034 files changed
+243381
No files matched your search
@@ -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 === ":");
|
||||
Reference in new issue
Заблокувати користувача