Keeping LSP client alive

Hi I am using GitHub - oxalica/async-lsp: Asynchronous Language Server Protocol framework to build a connection to a LSP server.
I am trying to build a function that returns the LSP client for use in my application. My problem is that the connection is dropped as soon as I call the server outside of my main function.

I have the following code in language_server_client.rs:

pub async fn get_client() -> ServerSocket {
    let root_dir = Path::new(TEST_ROOT)
        .canonicalize()
        .expect("test root should be valid");

    let (indexed_tx, _indexed_rx) = oneshot::channel();

    let (mainloop, mut server) = async_lsp::MainLoop::new_client(|_server| {
        let mut router = Router::new(ClientState {
            indexed_tx: Some(indexed_tx),
        });
        router
            .notification::<PublishDiagnostics>(|_, _| ControlFlow::Continue(()))
            .notification::<ShowMessage>(|_, params| {
                info!("Message {:?}: {}", params.typ, params.message);
                ControlFlow::Continue(())
            })
            .unhandled_notification(|_, _| ControlFlow::Continue(()))
            // .unhandled_request(|_, _| )
            .unhandled_event(|_, _| ControlFlow::Continue(()))
            .event(|_, _: Stop| ControlFlow::Break(Ok(())));

        ServiceBuilder::new()
            .layer(TracingLayer::default())
            .layer(CatchUnwindLayer::default())
            .layer(ConcurrencyLayer::default())
            .service(router)
    });

    let child = async_process::Command::new(
        "rust-analyzer",
    )
    // .args([])
    .current_dir(&root_dir)
    .stdin(Stdio::piped())
    .stdout(Stdio::piped())
    .stderr(Stdio::inherit())
    .kill_on_drop(true)
    .spawn()
    .expect("Failed run rzls");
    let stdout = child.stdout.unwrap();
    let stdin = child.stdin.unwrap();

    let _mainloop_fut = tokio::spawn(async move { mainloop.run_buffered(stdout, stdin).await });

    server
        .initialize(InitializeParams {
            workspace_folders: Some(vec![WorkspaceFolder {
                uri: Url::from_file_path(&root_dir).unwrap(),
                name: "root".into(),
            }]),
            capabilities: ClientCapabilities {
                window: Some(WindowClientCapabilities {
                    work_done_progress: Some(true),
                    ..WindowClientCapabilities::default()
                }),
                ..ClientCapabilities::default()
            },
            ..InitializeParams::default()
        })
        .await
        .unwrap();
    server.initialized(InitializedParams {}).unwrap();

    server
}

This is how I use the server in another file:

async fn main_loop(
    connection: Connection,
    params: serde_json::Value,
    client: &mut ServerSocket,
) -> Result<()> {
    client.initialized(InitializedParams {}).unwrap();
}

pub async fn start_lsp() -> Result<()> {
    let mut client = get_client().await;

    main_loop(connection, initialization_params, &mut client).await?;
    // ...
}

However, I keep getting a ServiceStopped Error. I guess the reason is that the child connection is dropped after get_client has been called, but I can't figure out how to solve the problem.

Ideally I would like the to have a struct that I can use for communicating with the server, something like this:

    let mut client = LanguageServerClient::new().await;
    client.initialize().await;
    
    // When calling client in this function, the connection has been dropped.
    some_function(client);

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.