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}