fix(tui): empty states, context, and minor improvements to diff viewer (#28878)
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user