client.rs

  1use std::ops::ControlFlow;
  2use std::path::PathBuf;
  3
  4use async_lsp::lsp_types::notification::{Progress, PublishDiagnostics, ShowMessage};
  5use async_lsp::lsp_types::{
  6    ClientCapabilities, DiagnosticClientCapabilities, InitializeParams, InitializedParams,
  7    TextDocumentClientCapabilities, Url, WindowClientCapabilities, WorkspaceFolder,
  8};
  9use async_lsp::router::Router;
 10use async_lsp::{LanguageServer, MainLoop, ServerSocket as LSPServerSocket};
 11use async_process::{Child, Command as ProcessCommand, Stdio};
 12use futures::channel::oneshot;
 13use tower::ServiceBuilder;
 14
 15pub struct LSPClientState {
 16    pub indexed_tx: Option<oneshot::Sender<()>>,
 17}
 18
 19mod control_flow_state {
 20    pub struct Stop;
 21}
 22
 23pub struct LSPClient {
 24    child: Child,
 25    pub mainloop: MainLoop<Router<LSPClientState>>,
 26    pub server: LSPServerSocket,
 27}
 28
 29impl LSPClient {
 30    pub async fn setup(
 31        lsp_command: &str,
 32        lsp_args: &[String],
 33        project_root: &PathBuf,
 34    ) -> anyhow::Result<Self> {
 35        let child = ProcessCommand::new(lsp_command)
 36            .args(lsp_args)
 37            .current_dir(project_root)
 38            .stdin(Stdio::piped())
 39            .stdout(Stdio::piped())
 40            .stderr(Stdio::inherit())
 41            .kill_on_drop(true)
 42            .spawn()
 43            .expect(format!("Failed to start lsp: {} {:?}", lsp_command, lsp_args).as_ref());
 44
 45        let (mainloop, mut server) = async_lsp::MainLoop::new_client(|_server| {
 46            let mut router = Router::new(LSPClientState { indexed_tx: None });
 47
 48            router
 49                .notification::<Progress>(|_this, _params| ControlFlow::Continue(()))
 50                .notification::<PublishDiagnostics>(|_this, _params| ControlFlow::Continue(()))
 51                .notification::<ShowMessage>(|_this, _params| ControlFlow::Continue(()))
 52                .event(|_, _: control_flow_state::Stop| ControlFlow::Break(Ok(())));
 53
 54            ServiceBuilder::new().service(router)
 55        });
 56
 57        let project_root_canonicalized = tokio::fs::canonicalize(project_root)
 58            .await
 59            .expect("Failed to canonicalize project root");
 60
 61        let uri = Url::from_file_path(&project_root_canonicalized)
 62            .expect("Failed to create URL from project_root path");
 63
 64        let _init_ret = server
 65            .initialize(InitializeParams {
 66                workspace_folders: Some(vec![WorkspaceFolder {
 67                    uri: uri,
 68                    name: "root".into(),
 69                }]),
 70                capabilities: ClientCapabilities {
 71                    window: Some(WindowClientCapabilities {
 72                        work_done_progress: Some(true),
 73                        ..WindowClientCapabilities::default()
 74                    }),
 75                    text_document: Some(TextDocumentClientCapabilities {
 76                        diagnostic: Some(DiagnosticClientCapabilities {
 77                            ..DiagnosticClientCapabilities::default()
 78                        }),
 79                        ..TextDocumentClientCapabilities::default()
 80                    }),
 81                    ..ClientCapabilities::default()
 82                },
 83                ..InitializeParams::default()
 84            })
 85            .await
 86            .unwrap();
 87
 88        server
 89            .initialized(InitializedParams {})
 90            .expect("Bad response to Initialized message");
 91
 92        Ok(Self {
 93            child,
 94            mainloop,
 95            server,
 96        })
 97    }
 98
 99    pub async fn run_main_loop(mut self) -> anyhow::Result<()> {
100        let stdin = self.child.stdin.take().unwrap();
101        let stdout = self.child.stdout.take().unwrap();
102        self.mainloop.run_buffered(stdout, stdin).await?;
103        Ok(())
104    }
105}