Expand description
Dynamic capability tracking (LSP §3.18.10.3 / 4.4.n).
After initialize, a server may send client/registerCapability
to announce capabilities it didn’t list in the initial
ServerCapabilities blob, or to attach method-specific options
that the static shape can’t express (the canonical example is
workspace/didChangeWatchedFiles – the glob patterns to watch
depend on the active project and aren’t known at handshake).
client/unregisterCapability reverses an earlier registration.
Before 4.4.n the actor accepted both requests with null and
threw the payload away; feature dispatch saw only the static
capability set. This module owns the “dynamic layer” the
server adds on top – the Capabilities
aggregate carries one of these and every supports_* probe
that cares about dynamic-only registrations consults it
alongside the static ServerCapabilities field.
§Indexing
The registry indexes registrations two ways:
by_id(HashMap<String, DynamicRegistration>) – sounregisterCapabilitycan find an entry by the id the server picked at register time and evict it in O(1).by_method(HashMap<String, Vec<String>>) – so feature dispatch can ask “istextDocument/completionregistered dynamically?” without scanning the whole table. The vec holds registration ids; the registry stays a single source of truth (the actualDynamicRegistrationlives only inby_id).
§Snapshot model
Capabilities is published through an
arc_swap::ArcSwap cell on the ServerHandle; readers see
the union of static + dynamic atomically. Mutations are
per-actor (only the actor task issues the swap), so writes
don’t race with each other. The cloning cost on update is
the size of the registry’s two HashMaps – typically
single-digit entries for the lifetime of a server, so the
arithmetic is cheap.
Structs§
- Dynamic
Registration - One server-issued capability registration. The
register_optionsblob’s interpretation is method-specific; the registry treats it as opaque JSON and hands it to the consumer when they fetch the entry. For methods we wire (e.g.workspace/didChangeWatchedFiles) the consumer parses it into the lsp_types shape on demand. - Dynamic
Registry - In-memory index of every active dynamic registration for one
server actor. Empty on a fresh server; mutated by the actor on
client/(un)registerCapabilityand snapshotted into the publishedCapabilitieson every change.