on master help sign in
lsp: Don't send notifications until initialize completes
46f3c69Blaž Hrastnik
helix-lsp/src/lib.rs+49−49
@@ -226,6 +226,8 @@ impl MethodCall {
226
226
227#[derive(Debug, PartialEq, Clone)]
227#[derive(Debug, PartialEq, Clone)]
228pub enum Notification {
228pub enum Notification {
229+ // we inject this notification to signal the LSP is ready
230+ Initialized,
229 PublishDiagnostics(lsp::PublishDiagnosticsParams),
231 PublishDiagnostics(lsp::PublishDiagnosticsParams),
230 ShowMessage(lsp::ShowMessageParams),
232 ShowMessage(lsp::ShowMessageParams),
231 LogMessage(lsp::LogMessageParams),
233 LogMessage(lsp::LogMessageParams),
@@ -237,6 +239,7 @@ impl Notification {
237 use lsp::notification::Notification as _;
239 use lsp::notification::Notification as _;
238
240
239 let notification = match method {
241 let notification = match method {
242+ lsp::notification::Initialized::METHOD => Self::Initialized,
240 lsp::notification::PublishDiagnostics::METHOD => {
243 lsp::notification::PublishDiagnostics::METHOD => {
241 let params: lsp::PublishDiagnosticsParams = params
244 let params: lsp::PublishDiagnosticsParams = params
242 .parse()
245 .parse()
@@ -294,7 +297,7 @@ impl Registry {
294 }
297 }
295 }
298 }
296
299
297− pub fn get_by_id(&mut self, id: usize) -> Option<&Client> {
300+ pub fn get_by_id(&self, id: usize) -> Option<&Client> {
298 self.inner
301 self.inner
299 .values()
302 .values()
300 .find(|(client_id, _)| client_id == &id)
303 .find(|(client_id, _)| client_id == &id)
@@ -302,55 +305,52 @@ impl Registry {
302 }
305 }
303
306
304 pub fn get(&mut self, language_config: &LanguageConfiguration) -> Result<Arc<Client>> {
307 pub fn get(&mut self, language_config: &LanguageConfiguration) -> Result<Arc<Client>> {
305− if let Some(config) = &language_config.language_server {
308+ let config = match &language_config.language_server {
306− // avoid borrow issues
309+ Some(config) => config,
307− let inner = &mut self.inner;
310+ None => return Err(Error::LspNotDefined),
308− let s_incoming = &mut self.incoming;
311+ };
309−
312+
310− match inner.entry(language_config.scope.clone()) {
313+ match self.inner.entry(language_config.scope.clone()) {
311− Entry::Occupied(entry) => Ok(entry.get().1.clone()),
314+ Entry::Occupied(entry) => Ok(entry.get().1.clone()),
312− Entry::Vacant(entry) => {
315+ Entry::Vacant(entry) => {
313− // initialize a new client
316+ // initialize a new client
314− let id = self.counter.fetch_add(1, Ordering::Relaxed);
317+ let id = self.counter.fetch_add(1, Ordering::Relaxed);
315− let (client, incoming, initialize_notify) = Client::start(
318+ let (client, incoming, initialize_notify) = Client::start(
316− &config.command,
319+ &config.command,
317− &config.args,
320+ &config.args,
318− serde_json::from_str(language_config.config.as_deref().unwrap_or("")).ok(),
321+ serde_json::from_str(language_config.config.as_deref().unwrap_or("")).ok(),
319− id,
322+ id,
320− )?;
323+ )?;
321− s_incoming.push(UnboundedReceiverStream::new(incoming));
324+ self.incoming.push(UnboundedReceiverStream::new(incoming));
322− let client = Arc::new(client);
325+ let client = Arc::new(client);
323−
326+
324− let _client = client.clone();
325− // Initialize the client asynchronously
327+ // Initialize the client asynchronously
328+ let _client = client.clone();
326− tokio::spawn(async move {
329+ tokio::spawn(async move {
327− use futures_util::TryFutureExt;
330+ use futures_util::TryFutureExt;
328− let value = _client
331+ let value = _client
329− .capabilities
332+ .capabilities
330− .get_or_try_init(|| {
333+ .get_or_try_init(|| {
331− _client
334+ _client
332− .initialize()
335+ .initialize()
333− .map_ok(|response| response.capabilities)
336+ .map_ok(|response| response.capabilities)
334− })
337+ })
335− .await;
338+ .await;
336−
339+
337− value.expect("failed to initialize capabilities");
340+ value.expect("failed to initialize capabilities");
338−
341+
339− // next up, notify<initialized>
342+ // next up, notify<initialized>
340− _client
343+ _client
341− .notify::<lsp::notification::Initialized>(lsp::InitializedParams {})
344+ .notify::<lsp::notification::Initialized>(lsp::InitializedParams {})
342− .await
345+ .await
343− .unwrap();
346+ .unwrap();
344−
347+
345− initialize_notify.notify_one();
348+ initialize_notify.notify_one();
346− });
349+ });
347−
350+
348− entry.insert((id, client.clone()));
351+ entry.insert((id, client.clone()));
349− Ok(client)
352+ Ok(client)
350− }
351 }
353 }
352− } else {
353− Err(Error::LspNotDefined)
354 }
354 }
355 }
355 }
356
356
helix-lsp/src/transport.rs+28−1
@@ -64,11 +64,16 @@ impl Transport {
64
64
65 let transport = Arc::new(transport);
65 let transport = Arc::new(transport);
66
66
67− tokio::spawn(Self::recv(transport.clone(), server_stdout, client_tx));
67+ tokio::spawn(Self::recv(
68+ transport.clone(),
69+ server_stdout,
70+ client_tx.clone(),
71+ ));
68 tokio::spawn(Self::err(transport.clone(), server_stderr));
72 tokio::spawn(Self::err(transport.clone(), server_stderr));
69 tokio::spawn(Self::send(
73 tokio::spawn(Self::send(
70 transport,
74 transport,
71 server_stdin,
75 server_stdin,
76+ client_tx,
72 client_rx,
77 client_rx,
73 notify.clone(),
78 notify.clone(),
74 ));
79 ));
@@ -269,6 +274,7 @@ impl Transport {
269 async fn send(
274 async fn send(
270 transport: Arc<Self>,
275 transport: Arc<Self>,
271 mut server_stdin: BufWriter<ChildStdin>,
276 mut server_stdin: BufWriter<ChildStdin>,
277+ mut client_tx: UnboundedSender<(usize, jsonrpc::Call)>,
272 mut client_rx: UnboundedReceiver<Payload>,
278 mut client_rx: UnboundedReceiver<Payload>,
273 initialize_notify: Arc<Notify>,
279 initialize_notify: Arc<Notify>,
274 ) {
280 ) {
@@ -303,6 +309,22 @@ impl Transport {
303 _ = initialize_notify.notified() => { // TODO: notified is technically not cancellation safe
309 _ = initialize_notify.notified() => { // TODO: notified is technically not cancellation safe
304 // server successfully initialized
310 // server successfully initialized
305 is_pending = false;
311 is_pending = false;
312+
313+ use lsp_types::notification::Notification;
314+ // Hack: inject an initialized notification so we trigger code that needs to happen after init
315+ let notification = ServerMessage::Call(jsonrpc::Call::Notification(jsonrpc::Notification {
316+ jsonrpc: None,
317+
318+ method: lsp_types::notification::Initialized::METHOD.to_string(),
319+ params: jsonrpc::Params::None,
320+ }));
321+ match transport.process_server_message(&mut client_tx, notification).await {
322+ Ok(_) => {}
323+ Err(err) => {
324+ error!("err: <- {:?}", err);
325+ }
326+ }
327+
306 // drain the pending queue and send payloads to server
328 // drain the pending queue and send payloads to server
307 for msg in pending_messages.drain(..) {
329 for msg in pending_messages.drain(..) {
308 log::info!("Draining pending message {:?}", msg);
330 log::info!("Draining pending message {:?}", msg);
@@ -317,6 +339,11 @@ impl Transport {
317 msg = client_rx.recv() => {
339 msg = client_rx.recv() => {
318 if let Some(msg) = msg {
340 if let Some(msg) = msg {
319 if is_pending && !is_initialize(&msg) {
341 if is_pending && !is_initialize(&msg) {
342+ // ignore notifications
343+ if let Payload::Notification(_) = msg {
344+ continue;
345+ }
346+
320 log::info!("Language server not initialized, delaying request");
347 log::info!("Language server not initialized, delaying request");
321 pending_messages.push(msg);
348 pending_messages.push(msg);
322 } else {
349 } else {
helix-term/src/application.rs+31
@@ -275,6 +275,37 @@ impl Application {
275 };
275 };
276
276
277 match notification {
277 match notification {
278+ Notification::Initialized => {
279+ let language_server =
280+ match self.editor.language_servers.get_by_id(server_id) {
281+ Some(language_server) => language_server,
282+ None => {
283+ warn!("can't find language server with id `{}`", server_id);
284+ return;
285+ }
286+ };
287+
288+ let docs = self.editor.documents().filter(|doc| {
289+ doc.language_server().map(|server| server.id()) == Some(server_id)
290+ });
291+
292+ // trigger textDocument/didOpen for docs that are already open
293+ for doc in docs {
294+ // TODO: extract and share with editor.open
295+ let language_id = doc
296+ .language()
297+ .and_then(|s| s.split('.').last()) // source.rust
298+ .map(ToOwned::to_owned)
299+ .unwrap_or_default();
300+
301+ tokio::spawn(language_server.text_document_did_open(
302+ doc.url().unwrap(),
303+ doc.version(),
304+ doc.text(),
305+ language_id,
306+ ));
307+ }
308+ }
278 Notification::PublishDiagnostics(params) => {
309 Notification::PublishDiagnostics(params) => {
279 let path = params.uri.to_file_path().unwrap();
310 let path = params.uri.to_file_path().unwrap();
280 let doc = self.editor.document_by_path_mut(&path);
311 let doc = self.editor.document_by_path_mut(&path);
helix-view/src/editor.rs+3−2
@@ -255,20 +255,21 @@ impl Editor {
255 .and_then(|language| self.language_servers.get(language).ok());
255 .and_then(|language| self.language_servers.get(language).ok());
256
256
257 if let Some(language_server) = language_server {
257 if let Some(language_server) = language_server {
258− doc.set_language_server(Some(language_server.clone()));
259−
260 let language_id = doc
258 let language_id = doc
261 .language()
259 .language()
262 .and_then(|s| s.split('.').last()) // source.rust
260 .and_then(|s| s.split('.').last()) // source.rust
263 .map(ToOwned::to_owned)
261 .map(ToOwned::to_owned)
264 .unwrap_or_default();
262 .unwrap_or_default();
265
263
264+ // TODO: this now races with on_init code if the init happens too quickly
266 tokio::spawn(language_server.text_document_did_open(
265 tokio::spawn(language_server.text_document_did_open(
267 doc.url().unwrap(),
266 doc.url().unwrap(),
268 doc.version(),
267 doc.version(),
269 doc.text(),
268 doc.text(),
270 language_id,
269 language_id,
271 ));
270 ));
271+
272+ doc.set_language_server(Some(language_server));
272 }
273 }
273
274
274 let id = self.documents.insert(doc);
275 let id = self.documents.insert(doc);