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