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, ServerSocket as LSPServerSocket};
11use async_process::{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 async fn setup_lsp_client(
24 lsp_command: &str,
25 lsp_args: &[String],
26 project_root: &PathBuf,
27) -> Result<(tokio::task::JoinHandle<()>, LSPServerSocket), Box<dyn std::error::Error>> {
28 let mut child = ProcessCommand::new(lsp_command)
29 .args(lsp_args)
30 .current_dir(project_root)
31 .stdin(Stdio::piped())
32 .stdout(Stdio::piped())
33 .stderr(Stdio::inherit())
34 .kill_on_drop(true)
35 .spawn()
36 .expect(format!("Failed to start lsp: {} {:?}", lsp_command, lsp_args).as_ref());
37
38 let stdin = child.stdin.take().unwrap();
39 let stdout = child.stdout.take().unwrap();
40
41 let (mainloop, mut server) = async_lsp::MainLoop::new_client(|_server| {
42 let mut router = Router::new(LSPClientState { indexed_tx: None });
43
44 router
45 .notification::<Progress>(|_this, _params| ControlFlow::Continue(()))
46 .notification::<PublishDiagnostics>(|_this, _params| ControlFlow::Continue(()))
47 .notification::<ShowMessage>(|_this, _params| ControlFlow::Continue(()))
48 .event(|_, _: control_flow_state::Stop| ControlFlow::Break(Ok(())));
49
50 ServiceBuilder::new().service(router)
51 });
52
53 let mainloop_fut = tokio::spawn(async move {
54 mainloop.run_buffered(stdout, stdin).await.unwrap();
55 });
56
57 let project_root_canonicalized = tokio::fs::canonicalize(project_root)
58 .await
59 .expect("Failed to canonicalize project root");
60 let uri = Url::from_file_path(&project_root_canonicalized).unwrap();
61 let _init_ret = server
62 .initialize(InitializeParams {
63 workspace_folders: Some(vec![WorkspaceFolder {
64 uri: uri,
65 name: "root".into(),
66 }]),
67 capabilities: ClientCapabilities {
68 window: Some(WindowClientCapabilities {
69 work_done_progress: Some(true),
70 ..WindowClientCapabilities::default()
71 }),
72 text_document: Some(TextDocumentClientCapabilities {
73 diagnostic: Some(DiagnosticClientCapabilities {
74 ..DiagnosticClientCapabilities::default()
75 }),
76 ..TextDocumentClientCapabilities::default()
77 }),
78 ..ClientCapabilities::default()
79 },
80 ..InitializeParams::default()
81 })
82 .await
83 .unwrap();
84 server.initialized(InitializedParams {}).unwrap();
85
86 Ok((mainloop_fut, server))
87}