progress
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
import z from "zod"
|
||||
import { lazy } from "../util/lazy"
|
||||
import { Filesystem } from "../util/filesystem"
|
||||
import path from "path"
|
||||
import { $ } from "bun"
|
||||
import { StorageNext } from "../storage/storage-next"
|
||||
import { Context } from "../util/context"
|
||||
|
||||
export namespace Project {
|
||||
export const Info = z.object({
|
||||
id: z.string(),
|
||||
worktree: z.string(),
|
||||
vcs: z.literal("git").optional(),
|
||||
})
|
||||
export type Info = z.infer<typeof Info>
|
||||
|
||||
const context = Context.create<Info>("project")
|
||||
|
||||
export const use = context.use
|
||||
export const provide = context.provide
|
||||
|
||||
const init = lazy(async () => {
|
||||
const cwd = process.cwd()
|
||||
const matches = Filesystem.up({ targets: [".git"], start: cwd })
|
||||
const git = await matches.next().then((x) => x.value)
|
||||
await matches.return()
|
||||
if (!git) {
|
||||
await StorageNext.write(["project", "global"], {
|
||||
id: "global",
|
||||
worktree: "/",
|
||||
})
|
||||
return
|
||||
}
|
||||
let worktree = path.dirname(git)
|
||||
const [id] = await $`git rev-list --max-parents=0 --all`
|
||||
.quiet()
|
||||
.nothrow()
|
||||
.cwd(worktree)
|
||||
.text()
|
||||
.then((x) =>
|
||||
x
|
||||
.split("\n")
|
||||
.filter(Boolean)
|
||||
.map((x) => x.trim())
|
||||
.toSorted(),
|
||||
)
|
||||
worktree = path.dirname(
|
||||
await $`git rev-parse --path-format=absolute --git-common-dir`
|
||||
.quiet()
|
||||
.nothrow()
|
||||
.cwd(worktree)
|
||||
.text()
|
||||
.then((x) => x.trim()),
|
||||
)
|
||||
await StorageNext.write<Info>(["project", id], {
|
||||
id,
|
||||
worktree,
|
||||
vcs: "git",
|
||||
})
|
||||
})
|
||||
|
||||
export async function list() {
|
||||
await init()
|
||||
const keys = await StorageNext.list(["project"])
|
||||
return await Promise.all(keys.map((x) => StorageNext.read<Info>(x)))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
export namespace State {
|
||||
interface Entry {
|
||||
state: any
|
||||
dispose?: (state: any) => Promise<void>
|
||||
}
|
||||
|
||||
const entries = new Map<string, Entry>()
|
||||
|
||||
export function create<S>(root: () => string, init: () => S, dispose?: (state: Awaited<S>) => Promise<void>) {
|
||||
return () => {
|
||||
const key = root()
|
||||
const exists = entries.get(key)
|
||||
if (exists) return exists.state as S
|
||||
const state = init()
|
||||
entries.set(key, {
|
||||
state,
|
||||
dispose,
|
||||
})
|
||||
return state
|
||||
}
|
||||
}
|
||||
|
||||
export async function dispose() {
|
||||
for (const [key, entry] of entries.entries()) {
|
||||
if (!entry.dispose) continue
|
||||
await entry.dispose(await entry.state)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import { Context } from "../util/context"
|
||||
|
||||
export namespace Worktree {
|
||||
const context = Context.create<string>("worktree")
|
||||
export const use = context.use
|
||||
export const provide = context.provide
|
||||
}
|
||||
Reference in New Issue
Block a user