chan_rs/
process.rs

1use std::{
2    collections::HashMap,
3    ffi::OsString,
4    future::Future,
5    ops::DerefMut,
6    path::{Path, PathBuf},
7    sync::Arc,
8};
9
10use lsp_types::{notification, request, CodeActionKind, ServerCapabilities};
11use parking_lot::{Mutex, RwLock};
12use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver};
13
14use crate::IOKind;
15use crate::{
16    io::{IoHandler, NotificationHandler, ResponseHandler, IO},
17    listener::Listener,
18    utils::Subscription,
19    AnyNotification,
20};
21
22/// Binary of the language server
23///
24/// * `path`: path to the executable
25/// * `envs`: List of environment variables
26/// * `args`: List of arguments for starting the process
27pub struct LanguageServerBinary {
28    pub path: PathBuf,
29    pub envs: Option<HashMap<String, String>>,
30    pub args: Vec<OsString>,
31}
32
33pub struct LanguageServer {
34    io: IO,
35    listener: Listener,
36    pub output_done_rx: UnboundedReceiver<String>,
37    code_action_kind: Option<Vec<CodeActionKind>>,
38    capabilities: RwLock<ServerCapabilities>,
39}
40
41impl LanguageServer {
42    /// Start a new language server process
43    /// A process is construct by one io_listener and one listener_loop
44    /// When sending something to the process, the request will be handled by background task
45    /// keeping the process lock-free
46    ///
47    /// # Usage
48    /// ``` rust
49    ///     let binary: LanguageServerBinary = LanguageServerBinary { ... };
50    ///     let root_path = Path::new("your-root");
51    ///     // Stderr capture will take every stderr response receivered
52    ///     // usefull for logging
53    ///     let stderr_capture = Arc::new(Mutex::new(...))
54    ///     let server =
55    ///         LanguageServerProcess::new(binary, 1, root_path, stderr_capture, None)?;
56    /// ```
57    /// * `binary`: See [LanguageServerBinary]
58    /// * `id`: id for the server
59    /// * `root_path`: Root path for the lsp, useful for discovering workspaces
60    /// * `capture`: Stderr capturer
61    /// * `code_action_kind`: List of code action kinds that will be registered during startup
62    pub fn new(
63        binary: LanguageServerBinary,
64        id: i32,
65        root_path: &Path,
66        capture: Arc<Mutex<Option<String>>>,
67        code_action_kind: Option<Vec<CodeActionKind>>,
68    ) -> anyhow::Result<Self> {
69        let (request_tx, request_rx) = unbounded_channel::<String>();
70        let (notification_tx, notification_rx) = unbounded_channel::<AnyNotification>();
71        let (output_done_tx, output_done_rx) = unbounded_channel();
72
73        let notification_handlers =
74            Arc::new(Mutex::new(HashMap::<_, NotificationHandler>::default()));
75        let response_handlers =
76            Arc::new(Mutex::new(Some(HashMap::<_, ResponseHandler>::default())));
77
78        let io_handlers = Arc::new(Mutex::new(HashMap::<_, IoHandler>::default()));
79
80        let io = IO::new(
81            id,
82            binary,
83            response_handlers.clone(),
84            io_handlers.clone(),
85            request_rx,
86            notification_tx,
87            output_done_tx,
88            root_path,
89            capture,
90        )?;
91        let listener = Listener::new(
92            notification_rx,
93            notification_handlers,
94            response_handlers,
95            io_handlers,
96            request_tx,
97        )?;
98
99        Ok(Self {
100            io,
101            listener,
102            output_done_rx,
103            code_action_kind,
104            capabilities: Default::default(),
105        })
106    }
107
108    /// Send a request to the server and get the response back
109    /// T must be type of [request::Request]. We had re-exported the module
110    ///
111    /// # Usage
112    /// ```rust
113    ///     use chan_rs::lsp_types::request::Initialize;
114    ///
115    ///     let init_params = IntializeParams::default();
116    ///     let response = server.request::<Initialize>(init_params)?;
117    /// ```
118    /// * `params`: Parameters for the request
119    pub async fn request<T: request::Request>(
120        &self,
121        params: T::Params,
122    ) -> anyhow::Result<T::Result> {
123        self.listener.request::<T>(params).await
124    }
125
126    /// Send a notify to the server, notify requests don't send response back
127    /// T must be type of [notification::Notification]. We had re-exported the module
128    ///
129    /// # Usage
130    /// ```rust
131    ///     use chan_rs::lsp_types::notification::Initialized;
132    ///
133    ///     let initialized = IntializedParams::default();
134    ///     server.notify::<Initialized>(initialized)?;
135    /// ```
136    ///
137    /// * `params`: Parameters for the notification
138    pub async fn notify<T: notification::Notification>(
139        &self,
140        params: T::Params,
141    ) -> anyhow::Result<()> {
142        self.listener.send_notification::<T>(params).await
143    }
144
145    ///
146    /// Register a handler to handle incoming request
147    /// You can only register on handler for one method
148    pub fn on_request<T: request::Request, F, Fut>(&self, f: F) -> Subscription
149    where
150        T::Params: 'static + Send,
151        F: Send + 'static + FnMut(T::Params) -> Fut,
152        Fut: Send + 'static + Future<Output = anyhow::Result<T::Result>>,
153    {
154        self.listener.on_request::<T, F, Fut>(f)
155    }
156
157    ///
158    /// Register a handler to handle incoming notification
159    /// You can only register one handler for one method
160    pub fn on_notification<T: notification::Notification, F>(&self, f: F) -> Subscription
161    where
162        T::Params: 'static + Send,
163        F: Send + 'static + FnMut(T::Params),
164    {
165        self.listener.on_notification::<T, F>(f)
166    }
167
168    /// Register a handler to handle stdio
169    /// You can re-regsiter the handler
170    ///
171    pub fn on_io<F>(&self, f: F) -> Subscription
172    where
173        F: Send + 'static + FnMut(IOKind, &str),
174    {
175        self.listener.on_io::<F>(f)
176    }
177
178    /// Get the server id
179    pub fn server_id(&self) -> i32 {
180        self.io.id()
181    }
182
183    /// Root Path of working project
184    pub fn root_path(&self) -> &PathBuf {
185        self.io.root_path()
186    }
187
188    /// Working dir of the workspace
189    pub fn working_dir(&self) -> &PathBuf {
190        self.io.working_dir()
191    }
192
193    /// List of capablilities
194    pub fn capabilities(&self) -> ServerCapabilities {
195        self.capabilities.read().clone()
196    }
197
198    /// Update the server capabilities
199    pub fn update_capabilities(&self, update: impl FnOnce(&mut ServerCapabilities)) {
200        update(self.capabilities.write().deref_mut())
201    }
202
203    /// List code action kinds
204    pub fn code_action_kinds(&self) -> Option<Vec<CodeActionKind>> {
205        self.code_action_kind.clone()
206    }
207
208    /// The server name
209    pub fn name(&self) -> &str {
210        self.io.name()
211    }
212
213    /// Kill the tasks
214    pub fn kill(&mut self) -> anyhow::Result<()> {
215        self.io.kill()?;
216        self.listener.kill()?;
217        Ok(())
218    }
219}