fix(tui): empty states, context, and minor improvements to diff viewer (#28878)

This commit is contained in:
James Long
2026-05-22 14:21:22 -04:00
committed by GitHub
parent d5068ba28e
commit ba746e36d8
13 changed files with 380 additions and 270 deletions
+44 -15
View File
@@ -11,6 +11,9 @@ const log = Log.create({ service: "vcs" })
const PATCH_CONTEXT_LINES = 2_147_483_647
const MAX_PATCH_BYTES = 10_000_000
const MAX_TOTAL_PATCH_BYTES = 10_000_000
type DiffOptions = {
readonly context?: number
}
const emptyPatch = (file: string) => formatPatch(structuredPatch(file, file, "", "", "", "", { context: 0 }))
@@ -91,11 +94,17 @@ const splitGitPatch = (patch: Git.Patch) => {
return chunks.slice(0, -1)
}
const batchPatches = Effect.fnUntraced(function* (git: Git.Interface, cwd: string, ref: string, list: Git.Item[]) {
const batchPatches = Effect.fnUntraced(function* (
git: Git.Interface,
cwd: string,
ref: string,
list: Git.Item[],
options?: DiffOptions,
) {
if (list.length === 0) return { patches: new Map<string, string>(), capped: false }
const result = yield* git.patchAll(cwd, ref, {
context: PATCH_CONTEXT_LINES,
context: options?.context ?? PATCH_CONTEXT_LINES,
maxOutputBytes: MAX_TOTAL_PATCH_BYTES,
})
if (result.truncated) log.warn("batched patch exceeded byte limit", { max: MAX_TOTAL_PATCH_BYTES })
@@ -116,11 +125,18 @@ const nativePatch = Effect.fnUntraced(function* (
cwd: string,
ref: string | undefined,
item: Git.Item,
options?: DiffOptions,
) {
const result =
item.code === "??" || !ref
? yield* git.patchUntracked(cwd, item.file, { context: PATCH_CONTEXT_LINES, maxOutputBytes: MAX_PATCH_BYTES })
: yield* git.patch(cwd, ref, item.file, { context: PATCH_CONTEXT_LINES, maxOutputBytes: MAX_PATCH_BYTES })
? yield* git.patchUntracked(cwd, item.file, {
context: options?.context ?? PATCH_CONTEXT_LINES,
maxOutputBytes: MAX_PATCH_BYTES,
})
: yield* git.patch(cwd, ref, item.file, {
context: options?.context ?? PATCH_CONTEXT_LINES,
maxOutputBytes: MAX_PATCH_BYTES,
})
if (!result.truncated && result.text) return result.text
if (result.truncated) log.warn("patch exceeded byte limit", { file: item.file, max: MAX_PATCH_BYTES })
@@ -140,13 +156,14 @@ const patchForItem = Effect.fnUntraced(function* (
item: Git.Item,
batch: { patches: Map<string, string>; capped: boolean },
capped: boolean,
options?: DiffOptions,
) {
if (capped) return emptyPatch(item.file)
const batched = batch.patches.get(item.file)
if (batched !== undefined) return batched
if (item.code !== "??" && batch.capped) return emptyPatch(item.file)
return yield* nativePatch(git, cwd, ref, item)
return yield* nativePatch(git, cwd, ref, item, options)
})
const files = Effect.fnUntraced(function* (
@@ -156,6 +173,7 @@ const files = Effect.fnUntraced(function* (
list: Git.Item[],
map: Map<string, { additions: number; deletions: number }>,
batch: { patches: Map<string, string>; capped: boolean },
options?: DiffOptions,
) {
const next: FileDiff[] = []
let total = 0
@@ -163,7 +181,7 @@ const files = Effect.fnUntraced(function* (
for (const item of list.toSorted((a, b) => a.file.localeCompare(b.file))) {
const stat = map.get(item.file) ?? (item.status === "added" ? yield* git.statUntracked(cwd, item.file) : undefined)
const patch = yield* patchForItem(git, cwd, ref, item, batch, capped)
const patch = yield* patchForItem(git, cwd, ref, item, batch, capped, options)
const result: { patch: string; capped: boolean } = capped
? { patch, capped: true }
: totalPatch(item.file, patch, total)
@@ -184,7 +202,12 @@ const files = Effect.fnUntraced(function* (
return next
})
const diffAgainstRef = Effect.fnUntraced(function* (git: Git.Interface, cwd: string, ref: string) {
const diffAgainstRef = Effect.fnUntraced(function* (
git: Git.Interface,
cwd: string,
ref: string,
options?: DiffOptions,
) {
const [list, stats, extra] = yield* Effect.all([git.diff(cwd, ref), git.stats(cwd, ref), git.status(cwd)], {
concurrency: 3,
})
@@ -197,13 +220,19 @@ const diffAgainstRef = Effect.fnUntraced(function* (git: Git.Interface, cwd: str
extra.filter((item) => item.code === "??"),
),
nums(stats),
yield* batchPatches(git, cwd, ref, list),
yield* batchPatches(git, cwd, ref, list, options),
options,
)
})
const track = Effect.fnUntraced(function* (git: Git.Interface, cwd: string, ref: string | undefined) {
if (!ref) return yield* files(git, cwd, ref, yield* git.status(cwd), new Map(), emptyBatch())
return yield* diffAgainstRef(git, cwd, ref)
const track = Effect.fnUntraced(function* (
git: Git.Interface,
cwd: string,
ref: string | undefined,
options?: DiffOptions,
) {
if (!ref) return yield* files(git, cwd, ref, yield* git.status(cwd), new Map(), emptyBatch(), options)
return yield* diffAgainstRef(git, cwd, ref, options)
})
export const Mode = Schema.Literals(["git", "branch"])
@@ -264,7 +293,7 @@ export interface Interface {
readonly branch: () => Effect.Effect<string | undefined>
readonly defaultBranch: () => Effect.Effect<string | undefined>
readonly status: () => Effect.Effect<FileStatus[]>
readonly diff: (mode: Mode) => Effect.Effect<FileDiff[]>
readonly diff: (mode: Mode, options?: DiffOptions) => Effect.Effect<FileDiff[]>
readonly diffRaw: () => Effect.Effect<string>
readonly apply: (input: ApplyInput) => Effect.Effect<ApplyResult, PatchApplyError>
}
@@ -352,19 +381,19 @@ export const layer: Layer.Layer<Service, never, Git.Service | Bus.Service> = Lay
}),
)
}),
diff: Effect.fn("Vcs.diff")(function* (mode: Mode) {
diff: Effect.fn("Vcs.diff")(function* (mode: Mode, options?: DiffOptions) {
const value = yield* InstanceState.get(state)
const ctx = yield* InstanceState.context
if (ctx.project.vcs !== "git") return []
if (mode === "git") {
return yield* track(git, ctx.directory, (yield* git.hasHead(ctx.directory)) ? "HEAD" : undefined)
return yield* track(git, ctx.directory, (yield* git.hasHead(ctx.directory)) ? "HEAD" : undefined, options)
}
if (!value.root) return []
if (value.current && value.current === value.root.name) return []
const ref = yield* git.mergeBase(ctx.directory, value.root.ref)
if (!ref) return []
return yield* diffAgainstRef(git, ctx.directory, ref)
return yield* diffAgainstRef(git, ctx.directory, ref, options)
}),
diffRaw: Effect.fn("Vcs.diffRaw")(function* () {
const ctx = yield* InstanceState.context