Consolidate server state onto a single server-context struct#5
Open
nathanweir wants to merge 1 commit into
Open
Consolidate server state onto a single server-context struct#5nathanweir wants to merge 1 commit into
nathanweir wants to merge 1 commit into
Conversation
Previously CLEF's mutable state was scattered across defparameters in three packages: clef-lsp/server (*documents*, *workspace-root*, *handlers*, *client-capabilities*, *initialized*), clef-symbols (*lexical-scopes-by-file*, *symbol-refs-by-file*, *workspace-symbol-index*, *document-line-offsets*, *global-scope*), and clef-lsp/lifecycle (*loaded-systems*, *file-to-system*, *asd-files*). Resetting between tests meant juggling every one of them, and new handlers reaching across modules had to know which package owned each global. All of that persistent state now lives on a single SERVER-CONTEXT struct in a new clef-context package, bound to clef-context:*server*. Short symbol-macro aliases (ctx:documents, ctx:workspace-root, ctx:handlers, ctx:lexical-scopes, ctx:loaded-systems, ...) expand to struct-accessor reads on *server* so call sites read and write them as ordinary places, including with setf. Shutdown and test setup now replace *server* with a fresh context in one call via ctx:reset-context. The transient walker state in clef-symbols (*current-scope*, *current-package*) stays as dynamic specials -- it only matters during one build-file-symbol-map call and doesn't belong on the shared context. Handler registration for textDocument/documentHighlight, textDocument/signatureHelp, and workspace/symbol has moved out of inline sethandler calls at the bottom of each handler file and into register-handlers, matching the rest of the endpoints. All 63 tests pass. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
nathanweir
commented
Apr 12, 2026
|
|
||
| ;; Reprocess the symbol-map. This is terribly jank and inefficient to do on every single change; needs debounced at the very least | ||
| (slog :debug "[textDocument/didChange] Rebuilding symbol map for document: ~A..." document-uri) | ||
| (let ((start-time (get-internal-real-time))) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
CLEF's mutable server state used to live in defparameters scattered across three packages:
clef-lsp/server—*documents*,*workspace-root*,*handlers*,*client-capabilities*,*initialized*,*output-stream*clef-symbols—*lexical-scopes-by-file*,*symbol-refs-by-file*,*workspace-symbol-index*,*document-line-offsets*,*global-scope*clef-lsp/lifecycle—*loaded-systems*,*file-to-system*,*asd-files*Resetting between test runs meant juggling every one of them by hand, and any new handler that wanted to touch state from a different subsystem had to know which package owned which global. The boundaries between "shared server state" and "this module's private cache" were invisible at the call site.
This PR folds all of that persistent state onto a single
server-contextstruct held in a newclef-contextpackage, bound toclef-context:*server*. Short symbol-macro aliases (ctx:documents,ctx:workspace-root,ctx:handlers,ctx:lexical-scopes,ctx:loaded-systems, ...) expand to struct-accessor reads on*server*, so call sites read and write them as if they were ordinary variables — including withsetf:Shutdown and test setup now replace
*server*with a fresh context in one call viactx:reset-context, which atomically wipes every field instead of resetting them one by one (and forgetting a few).What stayed behind
Transient state used only while walking a parse tree —
*current-scope*and*current-package*inclef-symbols— remains as dynamic specials in that package. They're meaningful only inside a singlebuild-file-symbol-mapcall and don't belong on the shared context. Everything persistent moved; everything per-call stayed.Drive-by cleanups
While migrating handlers I also moved the inline
(setf (gethash ... clef-lsp/server:*handlers*) ...)registrations that were sitting at the bottom ofhighlight.lisp,signature-help.lisp, andworkspace/symbol.lispintoregister-handlersalongside the rest of the endpoints. There was no reason for those three to be registered out-of-band.Files touched
src/context.lisp— theserver-contextstruct,*server*,reset-context, and the symbol-macro aliasessrc/packages.lisp:clef-context, with all field accessors and short aliases exported. Existing packages now use(:local-nicknames (:ctx :clef-context))and drop their state exports.clef.asd— loadscontextright afterlog, before anything that touches server statesrc/lsp/server.lisp— all globals removed;resetjust callsctx:reset-contextsrc/symbols/init.lisp— persistent state removed; walker specials keptsrc/lsp/lifecycle/initialize.lisp/initialized.lisp— ASDF state and initialized flag now on contextsrc/lsp/document/andsrc/lsp/workspace/migrated to the new accessorstest/document-tests.lisp/test/lifecycle-tests.lisp— assertions reach throughclef-context:nowCLAUDE.md— architecture section rewritten to document the context pattern and state it as the rule for new fieldsDiff size: 20 files changed, +377 / −211.
Test plan
just test— all 63 tests pass on the refactor branchshutdown→initializecycle leaves no stale state (open a file, shutdown, re-initialize, confirm the old document/scope trees are gone).asdfile reload ondidSavestill repopulatesctx:loaded-systemsandctx:file-to-system🤖 Generated with Claude Code