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}