feat(plugin): bound vcs adapter diffs
This commit is contained in:
parent
10a71bae4a
commit
ceb5f3554e
4 changed files with 40 additions and 5 deletions
|
|
@ -10,6 +10,7 @@ import { AppProcess } from "./process"
|
||||||
import { VcsBackends } from "./vcs/backends"
|
import { VcsBackends } from "./vcs/backends"
|
||||||
import { VcsGit } from "./vcs/git"
|
import { VcsGit } from "./vcs/git"
|
||||||
import { VcsHg } from "./vcs/hg"
|
import { VcsHg } from "./vcs/hg"
|
||||||
|
import { MAX_TOTAL_PATCH_BYTES, PATCH_CONTEXT_LINES } from "./vcs/patch"
|
||||||
|
|
||||||
export { FileStatus, Mode }
|
export { FileStatus, Mode }
|
||||||
|
|
||||||
|
|
@ -60,7 +61,10 @@ const layer = Layer.effect(
|
||||||
diff: Effect.fn("Vcs.diff")(function* (mode: Mode, options?: DiffOptions) {
|
diff: Effect.fn("Vcs.diff")(function* (mode: Mode, options?: DiffOptions) {
|
||||||
const impl = yield* adapter()
|
const impl = yield* adapter()
|
||||||
if (!impl) return []
|
if (!impl) return []
|
||||||
return yield* impl.diff(mode, options)
|
return yield* impl.diff(mode, {
|
||||||
|
context: options?.context ?? PATCH_CONTEXT_LINES,
|
||||||
|
maxOutputBytes: MAX_TOTAL_PATCH_BYTES,
|
||||||
|
})
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
|
|
|
||||||
|
|
@ -81,10 +81,22 @@ function guard(type: string, make: () => Vcs.Adapter): Vcs.Adapter {
|
||||||
adapter.pipe(
|
adapter.pipe(
|
||||||
Effect.flatMap((impl) => impl.diff(mode, options)),
|
Effect.flatMap((impl) => impl.diff(mode, options)),
|
||||||
sanitize(type, "diff", decodeDiff),
|
sanitize(type, "diff", decodeDiff),
|
||||||
|
Effect.map((rows) => boundDiff(rows, options.maxOutputBytes)),
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function boundDiff(rows: readonly FileDiff.Info[], maxOutputBytes: number) {
|
||||||
|
let total = 0
|
||||||
|
return rows.map((row) => {
|
||||||
|
if (row.patch === undefined) return row
|
||||||
|
const bytes = Buffer.byteLength(row.patch)
|
||||||
|
if (total + bytes > maxOutputBytes) return { ...row, patch: undefined }
|
||||||
|
total += bytes
|
||||||
|
return row
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
function sanitize<A>(type: string, operation: string, decode: (input: unknown) => Option.Option<readonly A[]>) {
|
function sanitize<A>(type: string, operation: string, decode: (input: unknown) => Option.Option<readonly A[]>) {
|
||||||
return <E, R>(effect: Effect.Effect<readonly A[], E, R>) =>
|
return <E, R>(effect: Effect.Effect<readonly A[], E, R>) =>
|
||||||
effect.pipe(
|
effect.pipe(
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,23 @@ describe("VcsBackends", () => {
|
||||||
}).pipe(Effect.scoped, provide),
|
}).pipe(Effect.scoped, provide),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
it.live("passes normalized options and bounds plugin diffs", () =>
|
||||||
|
Effect.gen(function* () {
|
||||||
|
let received: Parameters<PluginVcs.Adapter["diff"]>[1] | undefined
|
||||||
|
yield* register(
|
||||||
|
backend({
|
||||||
|
diff: (_mode, options) => {
|
||||||
|
received = options
|
||||||
|
return Effect.succeed([{ ...diff[0], patch: "x".repeat(options.maxOutputBytes + 1) }])
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
const vcs = yield* Vcs.Service
|
||||||
|
expect(yield* vcs.diff("working", { context: 3 })).toEqual([{ ...diff[0], patch: undefined }])
|
||||||
|
expect(received).toEqual({ context: 3, maxOutputBytes: 10_000_000 })
|
||||||
|
}).pipe(Effect.scoped, provide),
|
||||||
|
)
|
||||||
|
|
||||||
it.live("passes the location scope to the adapter factory", () =>
|
it.live("passes the location scope to the adapter factory", () =>
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
let scope: PluginVcs.AdapterScope | undefined
|
let scope: PluginVcs.AdapterScope | undefined
|
||||||
|
|
|
||||||
|
|
@ -15,12 +15,14 @@ export interface AdapterScope {
|
||||||
readonly store: string
|
readonly store: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface DiffOptions {
|
||||||
|
readonly context: number
|
||||||
|
readonly maxOutputBytes: number
|
||||||
|
}
|
||||||
|
|
||||||
export interface Adapter {
|
export interface Adapter {
|
||||||
readonly status: () => Effect.Effect<readonly FileStatus[]>
|
readonly status: () => Effect.Effect<readonly FileStatus[]>
|
||||||
readonly diff: (
|
readonly diff: (mode: Mode, options: DiffOptions) => Effect.Effect<readonly FileDiff.Info[]>
|
||||||
mode: Mode,
|
|
||||||
options?: { readonly context?: number },
|
|
||||||
) => Effect.Effect<readonly FileDiff.Info[]>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Backend {
|
export interface Backend {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue