Skip to content

Notifications from one LSP break another #20

@vasigorc

Description

@vasigorc

I am using protols LSP together with metals within the same project: working on *.proto and *.scala files next to each other. protols relies on async-lsp's Router (link). When I have two file type buffers opened in nvim protols crashes with this message:

[ERROR][2025-04-08 16:36:17] .../vim/lsp/rpc.lua:770    "rpc"    "protols"    "stderr"    "\nthread 'main' panicked at <REDACTED>.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/protols-0.11.5/src/main.rs:94:46:\ncalled `Result::unwrap()` on an `Err` value: Routing(\"Unhandled notification: metals/didFocusTextDocument\")\nnote: run with `RUST_BACKTRACE=1` environment variable to display a backtrace\n"

This lands into this handler in async-lsp. Copying it below for eventual line changes on GitHub:

            unhandled_notif: Box::new(|_, notif| {
                if notif.method.starts_with("$/") {
                    ControlFlow::Continue(())
                } else {
                    ControlFlow::Break(Err(crate::Error::Routing(format!(
                        "Unhandled notification: {}",
                        notif.method,
                    ))))
                }
            }),

metals/didFocusTextDocument is valid albeit not a generic LSP notification, which is why it is not found in lsp-types (link) which is what async-lsp relies on to identify notifications as being handled or no:

    pub fn notification<N: Notification>(
        &mut self,
        handler: impl Fn(&mut St, N::Params) -> ControlFlow<Result<()>> + Send + 'static,
    ) -> &mut Self {
        self.notif_handlers.insert(
            N::METHOD,
            Box::new(
                move |state, notif| match serde_json::from_value::<N::Params>(notif.params) {
                    Ok(params) => handler(state, params),
                    Err(err) => ControlFlow::Break(Err(err.into())),
                },
            ),
        );
        self
    }

I am raising this issue with async-lsp and not with protols because there's not much thing that they can do in the case of an error, attempts to catch errors and restart server would be kind of hackish, in my opinion. Instead I think that async-lsp could make the handling more permissive. The current design forces tight coupling between different LSP implementations and limits interoperability

I realize that you don't want to be too permissive neither and that you would like to be backward compatible. Would it be possible to add a reserved setting (or reserved settings) to:

  • ignore unknown notifications (call ControlFlow::Continue)
  • if that is too wild, maybe add also a allow_list setting to specifically mention which notifications are ok to be ignored?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions