You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This is a bit eh in that it 'forces' the caller to do a big match statement over the method and then do the deserialization themselves. Ideally we would lessen that friction by default.
My initial thoughts are to have something vaguely like:
(I spell out types to make it more clear, but a lot of the callback types could be inferred)
// T is state.// Q: A shorter/better name for `ResponseSender`?typeRequestHandler<T,V> = Box<dynFn(&mutT,V,ResponseSender)>;typeNotifHandler<T,V> = Box<dynFn(&mutT,V)>;structMsgHandler<T>{request_handlers:HashMap<String,RequestHandler<T, serde_json::Value>>,notif_handlers:HashMap<String,NotifHandler<T, serde_json::Value>>}impl<T>MsgHandler<T>{pubfnnew() -> MsgHandler{/* ... */}// Deserialize bound is a bit trickier in reality, but basically this// Q: The error `_` should probably be serde_json's deserialize error type but perhaps it should be more general?pubfnon_request<V:Deserialize,R:Serialize>(mutself,method:implInto<String>,cb:implFn(&mutT,Result<V,_>,ResponseSender) + 'static) -> Self{// The outline of how request handlers work is that you're supposed to call the `send` function on `ResponseSender`` with your data.// (We can't have it simply be returned from the function, because sometimes you only want to run LSP commands *after* you respond, like in initialize)let cb = Box::new(move |state:&mutT,data: serde_json::Value,resp:ResponseSender| {let data:Result<V,_> = serde_json::from_value(data);cb(state, data, resp);});self.request_handlers.insert(method.into(), cb);}pubfnon_notif<V:Deserialize>(mutself,method:implInto<String>,cb:implFn(&mutT,Result<V,_>) + 'static) -> Self{let cb = Box::new(move |state:&mutT,data: serde_json::Value| {let data:Result<V,_> = serde_json::from_value(data);cb(state, data);});self.notif_handlers.insert(method.into(), cb);}/// Listen for the [`Initialize`] request./// ... common docs here so that plugin authors don't have to read the lsp spec ...pubfnon_initialize(mutself,cb:implFn(&mutT,Result<InitializeParams,_>,ResponseSender) + 'static) -> Self{self.on_request(Initialize::Method, cb)}// ...// Various common functions.// We might not want to implement functions for literally every random LSP feature. Just common ones.}pubstructResponseSender{// ... whatever is needed to send a response to the request ...// We could do FUN generic PhantomData to make so the closure can only send values of the allowed response type. I think that would be nice without adding complexity.// Q: we can log a warning if you didn't send a response/err back? Not sure if that's desirable in general.// sent_response: bool,}// ... obvious response sender impl ...
This would allow some common way of implementing a LSP startup to be like:
How do we say that we're using a specific message handler? There's no main function that the plugin can nicely 'start' in.
Do we want to allow dynamically registering handlers?
I lean towards yes, though do we alert the client editor about there being nothing handling the method?
RA logs warnings if it receives methods it doesn't handle. Maybe we should just automatically send out a command to log that, to encourage the plugin author to handle it nicely?
I'd say that what we can do is make the register_plugin have an alternate sort if you specify a 'handler creation function',
The register_plugin trait would simply implement handler automatically.
// Example possible output of register plugin when specifying a handler function.thread_local!{staticSTATE: std::cell::RefCell<State> = std::cell::RefCell::new(Default::default());staticHANDLER:OnceCell<RefCell<MsgHandler<State>>> = OnceCell::new();}fnmain(){}pubfnhandle_rpc(){// ... typical handle_rpc ...}implHandlerforState{fnhandle_request(&mutself,id:u64,method:String,params:Value){STATE.with(move |state| {HANDLER.get_or_init(|| RefCell::new(handler())).borrow_mut().handle_request(state, id, method, params)})}fnhandle_notification(&mutself,method:String,params:Value){STATE.with(move |state| {HANDLER.get_or_init(|| RefCell::new(handler())).borrow_mut().handle_notification(state, method, params)})}}
This would allow the existing plugin implementations to just automatically work without any changes, and would allow swapping it out for a completely custom handler if desired.
The text was updated successfully, but these errors were encountered:
Currently
LapcePlugin
is defined as:This is a bit eh in that it 'forces' the caller to do a big match statement over the method and then do the deserialization themselves. Ideally we would lessen that friction by default.
My initial thoughts are to have something vaguely like:
(I spell out types to make it more clear, but a lot of the callback types could be inferred)
This would allow some common way of implementing a LSP startup to be like:
There's now two questions:
I'd say that what we can do is make the
register_plugin
have an alternate sort if you specify a 'handler creation function',We also introduce a new trait that acts as trait which
LapcePlugin
requires.The
register_plugin
trait would simply implementhandler
automatically.This would allow the existing plugin implementations to just automatically work without any changes, and would allow swapping it out for a completely custom handler if desired.
The text was updated successfully, but these errors were encountered: