mirror of
https://seed.radicle.dev/rad:z4V1sjrXqjvFdnCUbxPFqd5p4DtH5.git
synced 2026-10-27 15:40:36 +08:00
Refactor commit and diff types, updates outdated diff types
- Adds support for modified moved files see https://github.com/radicle-dev/radicle-git/pull/141 Signed-off-by: Sebastian Martinez <me@sebastinez.dev>
This commit is contained in:
@@ -11,8 +11,6 @@ import type {
|
||||
Commit,
|
||||
CommitHeader,
|
||||
Diff,
|
||||
DiffAddedDeletedModifiedChangeset,
|
||||
DiffCopiedMovedChangeset,
|
||||
HunkLine,
|
||||
} from "./lib/project/commit.js";
|
||||
import type { Issue, IssueState } from "./lib/project/issue.js";
|
||||
@@ -44,8 +42,6 @@ export type {
|
||||
Commit,
|
||||
CommitHeader,
|
||||
Diff,
|
||||
DiffAddedDeletedModifiedChangeset,
|
||||
DiffCopiedMovedChangeset,
|
||||
DiffResponse,
|
||||
HunkLine,
|
||||
Issue,
|
||||
|
||||
+106
-102
@@ -1,171 +1,175 @@
|
||||
import type { ZodSchema } from "zod";
|
||||
import { array, literal, number, object, string, union } from "zod";
|
||||
import type { z } from "zod";
|
||||
import { array, literal, number, object, optional, string, union } from "zod";
|
||||
|
||||
interface GitPerson {
|
||||
name: string;
|
||||
email: string;
|
||||
}
|
||||
export { commitHeaderSchema, diffSchema, commitSchema, commitsSchema };
|
||||
|
||||
export type {
|
||||
Commits,
|
||||
HunkLine,
|
||||
Commit,
|
||||
DiffFile,
|
||||
Diff,
|
||||
DiffCopiedChangeset,
|
||||
DiffMovedChangeset,
|
||||
CommitHeader,
|
||||
DiffAddedChangeset,
|
||||
DiffDeletedChangeset,
|
||||
DiffModifiedChangeset,
|
||||
DiffContent,
|
||||
};
|
||||
|
||||
const gitPersonSchema = object({
|
||||
name: string(),
|
||||
email: string(),
|
||||
}) satisfies ZodSchema<GitPerson>;
|
||||
});
|
||||
|
||||
export interface CommitHeader {
|
||||
id: string;
|
||||
author: GitPerson;
|
||||
summary: string;
|
||||
description: string;
|
||||
parents: string[];
|
||||
committer: GitPerson & { time: number };
|
||||
}
|
||||
type CommitHeader = z.infer<typeof commitHeaderSchema>;
|
||||
|
||||
export const commitHeaderSchema = object({
|
||||
const commitHeaderSchema = object({
|
||||
id: string(),
|
||||
author: gitPersonSchema,
|
||||
summary: string(),
|
||||
description: string(),
|
||||
parents: array(string()),
|
||||
committer: gitPersonSchema.merge(object({ time: number() })),
|
||||
}) satisfies ZodSchema<CommitHeader>;
|
||||
});
|
||||
|
||||
interface AdditionHunkLine {
|
||||
line: string;
|
||||
lineNo: number;
|
||||
type: "addition";
|
||||
}
|
||||
type AdditionHunkLine = z.infer<typeof additionHunkLineSchema>;
|
||||
|
||||
const additionHunkLineSchema = object({
|
||||
line: string(),
|
||||
lineNo: number(),
|
||||
type: literal("addition"),
|
||||
}) satisfies ZodSchema<AdditionHunkLine>;
|
||||
});
|
||||
|
||||
interface DeletionHunkLine {
|
||||
line: string;
|
||||
lineNo: number;
|
||||
type: "deletion";
|
||||
}
|
||||
type DeletionHunkLine = z.infer<typeof deletionHunkLineSchema>;
|
||||
|
||||
const deletionHunkLineSchema = object({
|
||||
line: string(),
|
||||
lineNo: number(),
|
||||
type: literal("deletion"),
|
||||
}) satisfies ZodSchema<DeletionHunkLine>;
|
||||
});
|
||||
|
||||
interface ContextHunkLine {
|
||||
line: string;
|
||||
lineNoNew: number;
|
||||
lineNoOld: number;
|
||||
type: "context";
|
||||
}
|
||||
type DiffFile = z.infer<typeof diffFileSchema>;
|
||||
|
||||
const diffFileSchema = object({
|
||||
oid: string(),
|
||||
mode: union([
|
||||
literal("blob"),
|
||||
literal("blobExecutable"),
|
||||
literal("tree"),
|
||||
literal("link"),
|
||||
literal("commit"),
|
||||
]),
|
||||
});
|
||||
|
||||
type ContextHunkLine = z.infer<typeof contextHunkLineSchema>;
|
||||
|
||||
const contextHunkLineSchema = object({
|
||||
line: string(),
|
||||
lineNoNew: number(),
|
||||
lineNoOld: number(),
|
||||
type: literal("context"),
|
||||
}) satisfies ZodSchema<ContextHunkLine>;
|
||||
});
|
||||
|
||||
export type HunkLine = AdditionHunkLine | DeletionHunkLine | ContextHunkLine;
|
||||
type HunkLine = AdditionHunkLine | DeletionHunkLine | ContextHunkLine;
|
||||
|
||||
const hunkLineSchema = union([
|
||||
additionHunkLineSchema,
|
||||
deletionHunkLineSchema,
|
||||
contextHunkLineSchema,
|
||||
]) satisfies ZodSchema<HunkLine>;
|
||||
|
||||
interface ChangesetHunk {
|
||||
header: string;
|
||||
lines: HunkLine[];
|
||||
}
|
||||
]);
|
||||
|
||||
const changesetHunkSchema = object({
|
||||
header: string(),
|
||||
lines: array(hunkLineSchema),
|
||||
}) satisfies ZodSchema<ChangesetHunk>;
|
||||
});
|
||||
|
||||
export interface DiffAddedDeletedModifiedChangeset {
|
||||
path: string;
|
||||
diff: {
|
||||
type: "plain" | "binary" | "empty";
|
||||
hunks: ChangesetHunk[];
|
||||
eof: "noneMissing" | "oldMissing" | "newMissing" | "bothMissing";
|
||||
};
|
||||
}
|
||||
type DiffContent = z.infer<typeof diffContentSchema>;
|
||||
|
||||
const diffAddedDeletedModifiedChangesetSchema = object({
|
||||
const diffContentSchema = object({
|
||||
type: union([literal("plain"), literal("binary"), literal("empty")]),
|
||||
hunks: array(changesetHunkSchema),
|
||||
eof: union([
|
||||
literal("noneMissing"),
|
||||
literal("oldMissing"),
|
||||
literal("newMissing"),
|
||||
literal("bothMissing"),
|
||||
]),
|
||||
});
|
||||
|
||||
type DiffAddedChangeset = z.infer<typeof diffAddedChangesetSchema>;
|
||||
|
||||
const diffAddedChangesetSchema = object({
|
||||
path: string(),
|
||||
diff: object({
|
||||
type: union([literal("plain"), literal("binary"), literal("empty")]),
|
||||
hunks: array(changesetHunkSchema),
|
||||
eof: union([
|
||||
literal("noneMissing"),
|
||||
literal("oldMissing"),
|
||||
literal("newMissing"),
|
||||
literal("bothMissing"),
|
||||
]),
|
||||
}),
|
||||
}) satisfies ZodSchema<DiffAddedDeletedModifiedChangeset>;
|
||||
diff: diffContentSchema,
|
||||
new: diffFileSchema,
|
||||
});
|
||||
|
||||
export interface DiffCopiedMovedChangeset {
|
||||
newPath: string;
|
||||
oldPath: string;
|
||||
}
|
||||
type DiffDeletedChangeset = z.infer<typeof diffDeletedChangesetSchema>;
|
||||
|
||||
const diffCopiedMovedChangesetSchema = object({
|
||||
const diffDeletedChangesetSchema = object({
|
||||
path: string(),
|
||||
diff: diffContentSchema,
|
||||
old: diffFileSchema,
|
||||
});
|
||||
|
||||
type DiffModifiedChangeset = z.infer<typeof diffModifiedChangesetSchema>;
|
||||
|
||||
const diffModifiedChangesetSchema = object({
|
||||
path: string(),
|
||||
diff: diffContentSchema,
|
||||
new: diffFileSchema,
|
||||
old: diffFileSchema,
|
||||
});
|
||||
|
||||
type DiffCopiedChangeset = z.infer<typeof diffCopiedChangesetSchema>;
|
||||
|
||||
const diffCopiedChangesetSchema = object({
|
||||
newPath: string(),
|
||||
oldPath: string(),
|
||||
}) satisfies ZodSchema<DiffCopiedMovedChangeset>;
|
||||
});
|
||||
|
||||
export interface Diff {
|
||||
added: DiffAddedDeletedModifiedChangeset[];
|
||||
deleted: DiffAddedDeletedModifiedChangeset[];
|
||||
moved: DiffCopiedMovedChangeset[];
|
||||
copied: DiffCopiedMovedChangeset[];
|
||||
modified: DiffAddedDeletedModifiedChangeset[];
|
||||
stats: {
|
||||
filesChanged: number;
|
||||
insertions: number;
|
||||
deletions: number;
|
||||
};
|
||||
}
|
||||
type DiffMovedChangeset = z.infer<typeof diffMovedChangesetSchema>;
|
||||
|
||||
export const diffSchema = object({
|
||||
added: array(diffAddedDeletedModifiedChangesetSchema),
|
||||
deleted: array(diffAddedDeletedModifiedChangesetSchema),
|
||||
moved: array(diffCopiedMovedChangesetSchema),
|
||||
copied: array(diffCopiedMovedChangesetSchema),
|
||||
modified: array(diffAddedDeletedModifiedChangesetSchema),
|
||||
const diffMovedChangesetSchema = diffCopiedChangesetSchema.merge(
|
||||
object({
|
||||
old: optional(diffFileSchema),
|
||||
new: optional(diffFileSchema),
|
||||
diff: optional(diffContentSchema),
|
||||
}),
|
||||
);
|
||||
|
||||
type Diff = z.infer<typeof diffSchema>;
|
||||
|
||||
const diffSchema = object({
|
||||
added: array(diffAddedChangesetSchema),
|
||||
deleted: array(diffDeletedChangesetSchema),
|
||||
modified: array(diffModifiedChangesetSchema),
|
||||
moved: array(diffMovedChangesetSchema),
|
||||
copied: array(diffCopiedChangesetSchema),
|
||||
stats: object({
|
||||
filesChanged: number(),
|
||||
insertions: number(),
|
||||
deletions: number(),
|
||||
}),
|
||||
}) satisfies ZodSchema<Diff>;
|
||||
});
|
||||
|
||||
export interface Commit {
|
||||
commit: CommitHeader;
|
||||
diff: Diff;
|
||||
branches: string[];
|
||||
}
|
||||
type Commit = z.infer<typeof commitSchema>;
|
||||
|
||||
export const commitSchema = object({
|
||||
const commitSchema = object({
|
||||
commit: commitHeaderSchema,
|
||||
diff: diffSchema,
|
||||
branches: array(string()),
|
||||
}) satisfies ZodSchema<Commit>;
|
||||
});
|
||||
|
||||
export interface Commits {
|
||||
commits: Commit[];
|
||||
stats: { commits: number; branches: number; contributors: number };
|
||||
}
|
||||
type Commits = z.infer<typeof commitsSchema>;
|
||||
|
||||
export const commitsSchema = object({
|
||||
const commitsSchema = object({
|
||||
commits: array(commitSchema),
|
||||
stats: object({
|
||||
commits: number(),
|
||||
branches: number(),
|
||||
contributors: number(),
|
||||
}),
|
||||
}) satisfies ZodSchema<Commits>;
|
||||
});
|
||||
|
||||
@@ -72,25 +72,53 @@
|
||||
<FileDiff
|
||||
{projectId}
|
||||
{baseUrl}
|
||||
{file}
|
||||
{revision}
|
||||
filePath={file.path}
|
||||
fileDiff={file.diff}
|
||||
headerBadgeCaption="added" />
|
||||
{/each}
|
||||
{#each diff.deleted as file}
|
||||
<FileDiff
|
||||
{projectId}
|
||||
{baseUrl}
|
||||
{file}
|
||||
{revision}
|
||||
filePath={file.path}
|
||||
fileDiff={file.diff}
|
||||
headerBadgeCaption="deleted" />
|
||||
{/each}
|
||||
{#each diff.modified as file}
|
||||
<FileDiff {projectId} {baseUrl} {file} {revision} />
|
||||
<FileDiff
|
||||
{projectId}
|
||||
{baseUrl}
|
||||
{revision}
|
||||
filePath={file.path}
|
||||
fileDiff={file.diff} />
|
||||
{/each}
|
||||
{#each diff.moved as file}
|
||||
<FileLocationChange {projectId} {baseUrl} {file} {revision} mode="moved" />
|
||||
{#if file.diff}
|
||||
<FileDiff
|
||||
{projectId}
|
||||
{baseUrl}
|
||||
{revision}
|
||||
filePath={file.newPath}
|
||||
fileDiff={file.diff} />
|
||||
{:else}
|
||||
<FileLocationChange
|
||||
{projectId}
|
||||
{baseUrl}
|
||||
{revision}
|
||||
newPath={file.newPath}
|
||||
oldPath={file.oldPath}
|
||||
mode="moved" />
|
||||
{/if}
|
||||
{/each}
|
||||
{#each diff.copied as file}
|
||||
<FileLocationChange {projectId} {baseUrl} {file} {revision} mode="copied" />
|
||||
<FileLocationChange
|
||||
{projectId}
|
||||
{baseUrl}
|
||||
{revision}
|
||||
newPath={file.newPath}
|
||||
oldPath={file.oldPath}
|
||||
mode="copied" />
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
<script lang="ts">
|
||||
import { onDestroy, onMount } from "svelte";
|
||||
import type {
|
||||
BaseUrl,
|
||||
DiffAddedDeletedModifiedChangeset,
|
||||
HunkLine,
|
||||
} from "@httpd-client";
|
||||
import type { DiffContent } from "@httpd-client/lib/project/commit";
|
||||
import type { BaseUrl, HunkLine } from "@httpd-client";
|
||||
|
||||
import Badge from "@app/components/Badge.svelte";
|
||||
import Icon from "@app/components/Icon.svelte";
|
||||
import Link from "@app/components/Link.svelte";
|
||||
|
||||
export let file: DiffAddedDeletedModifiedChangeset;
|
||||
export let filePath: string;
|
||||
export let fileDiff: DiffContent;
|
||||
export let revision: string;
|
||||
export let headerBadgeCaption: "added" | "deleted" | undefined = undefined;
|
||||
export let baseUrl: BaseUrl;
|
||||
@@ -28,11 +26,9 @@
|
||||
if (selection) {
|
||||
document
|
||||
.getElementById(
|
||||
[
|
||||
file.path,
|
||||
"H" + selection.startHunk,
|
||||
"L" + selection.startLine,
|
||||
].join("-"),
|
||||
[filePath, "H" + selection.startHunk, "L" + selection.startLine].join(
|
||||
"-",
|
||||
),
|
||||
)
|
||||
?.scrollIntoView();
|
||||
}
|
||||
@@ -57,7 +53,7 @@
|
||||
function updateSelection() {
|
||||
const fragment = window.location.hash.substring(1);
|
||||
const match = fragment.match(/(.+):H(\d+)L(\d+)(H(\d+)L(\d+))?/);
|
||||
if (match && match[1] === file.path) {
|
||||
if (match && match[1] === filePath) {
|
||||
selection = {
|
||||
startHunk: parseInt(match[2]),
|
||||
startLine: parseInt(match[3]),
|
||||
@@ -139,7 +135,7 @@
|
||||
lineIdx: number,
|
||||
event: MouseEvent,
|
||||
): string {
|
||||
const path = file.path;
|
||||
const path = filePath;
|
||||
// single line selection
|
||||
if (!event.shiftKey) {
|
||||
return path + ":H" + hunkIdx + "L" + lineIdx;
|
||||
@@ -317,7 +313,7 @@
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id={file.path} class="wrapper">
|
||||
<div id={filePath} class="wrapper">
|
||||
<header class="header">
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
@@ -329,7 +325,7 @@
|
||||
{/if}
|
||||
</div>
|
||||
<div class="actions">
|
||||
<p class="txt-regular">{file.path}</p>
|
||||
<p class="txt-regular">{filePath}</p>
|
||||
{#if headerBadgeCaption === "added"}
|
||||
<Badge variant="positive">added</Badge>
|
||||
{:else if headerBadgeCaption === "deleted"}
|
||||
@@ -342,7 +338,7 @@
|
||||
resource: "project.source",
|
||||
project: projectId,
|
||||
node: baseUrl,
|
||||
path: file.path,
|
||||
path: filePath,
|
||||
revision,
|
||||
}}>
|
||||
<Icon name="browse" />
|
||||
@@ -351,10 +347,10 @@
|
||||
</header>
|
||||
{#if !collapsed}
|
||||
<main>
|
||||
{#if file.diff.type === "plain"}
|
||||
{#if file.diff.hunks.length > 0}
|
||||
{#if fileDiff.type === "plain"}
|
||||
{#if fileDiff.hunks.length > 0}
|
||||
<table class="diff" data-file-diff-select>
|
||||
{#each file.diff.hunks as hunk, hunkIdx}
|
||||
{#each fileDiff.hunks as hunk, hunkIdx}
|
||||
<tr
|
||||
class="diff-line hunk-header"
|
||||
class:selected={hunkHeaderSelected(selection, hunkIdx)}>
|
||||
@@ -370,7 +366,7 @@
|
||||
class={`diff-line type-${line.type}`}
|
||||
class:selected={isLineSelected(selection, hunkIdx, lineIdx)}>
|
||||
<td
|
||||
id={[file.path, "H" + hunkIdx, "L" + lineIdx].join("-")}
|
||||
id={[filePath, "H" + hunkIdx, "L" + lineIdx].join("-")}
|
||||
class="diff-line-number left"
|
||||
on:click={e => selectLine(hunkIdx, lineIdx, e)}>
|
||||
<div class="selection-indicator" />
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
<script lang="ts">
|
||||
import type { BaseUrl, DiffCopiedMovedChangeset } from "@httpd-client";
|
||||
import type { BaseUrl } from "@httpd-client";
|
||||
|
||||
import Badge from "@app/components/Badge.svelte";
|
||||
import Icon from "@app/components/Icon.svelte";
|
||||
import Link from "@app/components/Link.svelte";
|
||||
|
||||
export let file: DiffCopiedMovedChangeset;
|
||||
export let newPath: string;
|
||||
export let oldPath: string;
|
||||
export let revision: string;
|
||||
export let mode: "moved" | "copied";
|
||||
export let baseUrl: BaseUrl;
|
||||
@@ -40,10 +41,10 @@
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id={file.newPath} class="wrapper">
|
||||
<div id={newPath} class="wrapper">
|
||||
<header class="header">
|
||||
<div class="actions">
|
||||
<p class="txt-regular">{file.oldPath} → {file.newPath}</p>
|
||||
<p class="txt-regular">{oldPath} → {newPath}</p>
|
||||
{#if mode === "moved"}
|
||||
<Badge variant="foreground">moved</Badge>
|
||||
{:else if mode === "copied"}
|
||||
@@ -56,7 +57,7 @@
|
||||
resource: "project.source",
|
||||
project: projectId,
|
||||
node: baseUrl,
|
||||
path: file.newPath,
|
||||
path: newPath,
|
||||
revision,
|
||||
}}>
|
||||
<Icon name="browse" />
|
||||
|
||||
Reference in New Issue
Block a user