Why Is It Designed This Way?
Why one mutable GlobalState?
VFS changes, request queues, diagnostics generations, configuration, and workspace reloads require a total order. Serial coordination is cheaper and clearer than distributed locks; expensive read derivation still runs elsewhere.
Why snapshots rather than locks around analysis?
Workers need a stable semantic world while the user keeps typing. Snapshots allow parallel reads and make cancellation/revision checks explicit instead of holding a global read lock that delays fresh input.
Why incremental queries?
An IDE repeatedly asks related questions after tiny edits. Dependency-tracked memoization reuses unaffected facts and computes only what a feature demands.
Why cancel old work aggressively?
A perfectly computed completion for yesterday’s cursor position has negative value. Unwinding stale queries frees scarce CPU for the latest revision and lets callers retry under an explicit policy.
Why run typing requests on the main thread?
Very small on-enter, matching-brace, or selection operations are latency sensitive. Queueing behind large semantic requests could cost more than the computation, provided they remain read-only and predictably cheap.
Why isolate formatting in one thread?
The editor may synchronously wait for formatting, but rustfmt can block. Its
own lane prevents both main-loop blocking and starvation behind analysis jobs.
Why keep syntax, semantics, IDE, and LSP types separate?
Each boundary has different stability and failure rules. Syntax should work without a project; semantics should not know paths; IDE values should describe editor concepts; wire compatibility belongs only to the LSP binary.
Why isolate proc macros in a process?
They execute arbitrary dynamic libraries and can crash or behave nondeterministically. A thread cannot contain a segfault or unload all bad process state; a child process can be restarted.