client.rs

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