diff options
| author | Adam <[email protected]> | 2026-02-06 09:37:49 -0600 |
|---|---|---|
| committer | GitHub <[email protected]> | 2026-02-06 09:37:49 -0600 |
| commit | a4bc883595df9ea0f752079519081bc602408553 (patch) | |
| tree | 583f21642f431899abe1dfb1f6bd9b2c01dc0206 /packages/app/src/context/comments.tsx | |
| parent | c07077f96c0019b2e18e0e8e1e0383deda08b3e6 (diff) | |
| download | opencode-a4bc883595df9ea0f752079519081bc602408553.tar.gz opencode-a4bc883595df9ea0f752079519081bc602408553.zip | |
chore: refactoring and tests (#12468)
Diffstat (limited to 'packages/app/src/context/comments.tsx')
| -rw-r--r-- | packages/app/src/context/comments.tsx | 152 |
1 files changed, 86 insertions, 66 deletions
diff --git a/packages/app/src/context/comments.tsx b/packages/app/src/context/comments.tsx index d51c16352..d43f3705b 100644 --- a/packages/app/src/context/comments.tsx +++ b/packages/app/src/context/comments.tsx @@ -1,8 +1,9 @@ -import { batch, createMemo, createRoot, onCleanup } from "solid-js" -import { createStore } from "solid-js/store" +import { batch, createEffect, createMemo, createRoot, onCleanup } from "solid-js" +import { createStore, reconcile, type SetStoreFunction, type Store } from "solid-js/store" import { createSimpleContext } from "@opencode-ai/ui/context" import { useParams } from "@solidjs/router" import { Persist, persisted } from "@/utils/persist" +import { createScopedCache } from "@/utils/scoped-cache" import type { SelectedLineRange } from "@/context/file" export type LineComment = { @@ -18,28 +19,28 @@ type CommentFocus = { file: string; id: string } const WORKSPACE_KEY = "__workspace__" const MAX_COMMENT_SESSIONS = 20 -type CommentSession = ReturnType<typeof createCommentSession> - -type CommentCacheEntry = { - value: CommentSession - dispose: VoidFunction +type CommentStore = { + comments: Record<string, LineComment[]> } -function createCommentSession(dir: string, id: string | undefined) { - const legacy = `${dir}/comments${id ? "/" + id : ""}.v1` +function aggregate(comments: Record<string, LineComment[]>) { + return Object.keys(comments) + .flatMap((file) => comments[file] ?? []) + .slice() + .sort((a, b) => a.time - b.time) +} - const [store, setStore, _, ready] = persisted( - Persist.scoped(dir, id, "comments", [legacy]), - createStore<{ - comments: Record<string, LineComment[]> - }>({ - comments: {}, - }), - ) +function insert(items: LineComment[], next: LineComment) { + const index = items.findIndex((item) => item.time > next.time) + if (index < 0) return [...items, next] + return [...items.slice(0, index), next, ...items.slice(index)] +} +function createCommentSessionState(store: Store<CommentStore>, setStore: SetStoreFunction<CommentStore>) { const [state, setState] = createStore({ focus: null as CommentFocus | null, active: null as CommentFocus | null, + all: aggregate(store.comments), }) const setFocus = (value: CommentFocus | null | ((value: CommentFocus | null) => CommentFocus | null)) => @@ -59,6 +60,7 @@ function createCommentSession(dir: string, id: string | undefined) { batch(() => { setStore("comments", input.file, (items) => [...(items ?? []), next]) + setState("all", (items) => insert(items, next)) setFocus({ file: input.file, id: next.id }) }) @@ -66,37 +68,72 @@ function createCommentSession(dir: string, id: string | undefined) { } const remove = (file: string, id: string) => { - setStore("comments", file, (items) => (items ?? []).filter((x) => x.id !== id)) - setFocus((current) => (current?.id === id ? null : current)) + batch(() => { + setStore("comments", file, (items) => (items ?? []).filter((item) => item.id !== id)) + setState("all", (items) => items.filter((item) => !(item.file === file && item.id === id))) + setFocus((current) => (current?.id === id ? null : current)) + }) } const clear = () => { batch(() => { - setStore("comments", {}) + setStore("comments", reconcile({})) + setState("all", []) setFocus(null) setActive(null) }) } - const all = createMemo(() => { - const files = Object.keys(store.comments) - const items = files.flatMap((file) => store.comments[file] ?? []) - return items.slice().sort((a, b) => a.time - b.time) - }) - return { - ready, list, - all, + all: () => state.all, add, remove, clear, - focus: createMemo(() => state.focus), + focus: () => state.focus, setFocus, clearFocus: () => setFocus(null), - active: createMemo(() => state.active), + active: () => state.active, setActive, clearActive: () => setActive(null), + reindex: () => setState("all", aggregate(store.comments)), + } +} + +export function createCommentSessionForTest(comments: Record<string, LineComment[]> = {}) { + const [store, setStore] = createStore<CommentStore>({ comments }) + return createCommentSessionState(store, setStore) +} + +function createCommentSession(dir: string, id: string | undefined) { + const legacy = `${dir}/comments${id ? "/" + id : ""}.v1` + + const [store, setStore, _, ready] = persisted( + Persist.scoped(dir, id, "comments", [legacy]), + createStore<CommentStore>({ + comments: {}, + }), + ) + const session = createCommentSessionState(store, setStore) + + createEffect(() => { + if (!ready()) return + session.reindex() + }) + + return { + ready, + list: session.list, + all: session.all, + add: session.add, + remove: session.remove, + clear: session.clear, + focus: session.focus, + setFocus: session.setFocus, + clearFocus: session.clearFocus, + active: session.active, + setActive: session.setActive, + clearActive: session.clearActive, } } @@ -105,44 +142,27 @@ export const { use: useComments, provider: CommentsProvider } = createSimpleCont gate: false, init: () => { const params = useParams() - const cache = new Map<string, CommentCacheEntry>() - - const disposeAll = () => { - for (const entry of cache.values()) { - entry.dispose() - } - cache.clear() - } - - onCleanup(disposeAll) - - const prune = () => { - while (cache.size > MAX_COMMENT_SESSIONS) { - const first = cache.keys().next().value - if (!first) return - const entry = cache.get(first) - entry?.dispose() - cache.delete(first) - } - } + const cache = createScopedCache( + (key) => { + const split = key.lastIndexOf("\n") + const dir = split >= 0 ? key.slice(0, split) : key + const id = split >= 0 ? key.slice(split + 1) : WORKSPACE_KEY + return createRoot((dispose) => ({ + value: createCommentSession(dir, id === WORKSPACE_KEY ? undefined : id), + dispose, + })) + }, + { + maxEntries: MAX_COMMENT_SESSIONS, + dispose: (entry) => entry.dispose(), + }, + ) + + onCleanup(() => cache.clear()) const load = (dir: string, id: string | undefined) => { - const key = `${dir}:${id ?? WORKSPACE_KEY}` - const existing = cache.get(key) - if (existing) { - cache.delete(key) - cache.set(key, existing) - return existing.value - } - - const entry = createRoot((dispose) => ({ - value: createCommentSession(dir, id), - dispose, - })) - - cache.set(key, entry) - prune() - return entry.value + const key = `${dir}\n${id ?? WORKSPACE_KEY}` + return cache.get(key).value } const session = createMemo(() => load(params.dir!, params.id)) |
