Description
At the moment static versus dynamic registration works quite differently.
Specifically:
- When you register a method dynamically, you have to provide the
RegistrationOptions
- When you register a method statically, you don't have to provide the
ServerCapability
(from Mappings for capabilities #581)- Instead, we try to infer the appropriate capability. See
inferServerCapabilities
- But, this requires a bunch of choices that we can't guess! So we put in a side channel in the form of
Options
, which basically lets us pass in things that need to go in the capabilities.
- Instead, we try to infer the appropriate capability. See
I think we could unify these. Specifically, I think we should make the user provide the capability in both cases. That means that we don't need to pass the extra information out-of-band in Options
, instead the user can just set things in the capability directly.
Of course, we probably want a way to provide a sensible default capability... but we also want this for the dynamically registered capabilities! So we should try and provide this for everything.
The main complication is that for both static and dynamic registration we may need to register several method handlers under the same capability, and what the capability is will depend on all of them. For example:
textDocument/completion
andcompletionItem/resolve
. The latter is not registered separately, but is rather indicated by a field in the completion capability.textDocument/semanticTokens/full
and the other semantic tokens methods. Similar.
I think we can do this reasonably nicely by pretending that we can register everything independently, and then merging all the capabilities when we actually do the registration. So for example, we might have a default capability for the textDocument/completion
handler that doesn't say it supports resolve, and then a capability for completionItem/resolve
that says it supports resolve, and then we merge those together into the final capability that we register. I think this should work for both static and dynamic capabilities.
So I think we want something like this:
-- We might not be able to have these actually instances due to singletons stuff, but morally we want them
instance Semigroup (ServerCapability m)
instance Semigroup (ClientCapability m)
instance Semigroup (RegistrationOptions m)
type StaticRegistration m = (Handler m, ServerCapability m)
type DynamicRegistration m = (Handler m, RegistrationOptions m)
We probably also need the server definition to tell us its static registrations as part of the initialize handler instead of beforehand. This is necessary anyway, since you might need to decide whether to use static or dynamic registration depending on the client's capabilities.
(It would also be nice if we could provide a relatively simple way to have a handler that is either statically or dynamically registered depending on whether the client supports dynamic registration...)