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