From 39b9a4bba2a026844348886c9d9f2026a3d6f658 Mon Sep 17 00:00:00 2001 From: Philipp Mildenberger Date: Wed, 5 Apr 2023 18:50:05 +0200 Subject: [PATCH] Add function `Editor::language_server_by_id` and refactor/simplify related code, also don't 'crash' in completion menu if language_server somehow disappeared --- helix-term/src/application.rs | 2 +- helix-term/src/commands/lsp.rs | 7 +++-- helix-term/src/ui/completion.rs | 47 ++++++++++++++++----------------- helix-view/src/editor.rs | 7 ++++- 4 files changed, 33 insertions(+), 30 deletions(-) diff --git a/helix-term/src/application.rs b/helix-term/src/application.rs index dbb873e0..40c6d8c6 100644 --- a/helix-term/src/application.rs +++ b/helix-term/src/application.rs @@ -665,7 +665,7 @@ impl Application { macro_rules! language_server { () => { - match self.editor.language_servers.get_by_id(server_id) { + match self.editor.language_server_by_id(server_id) { Some(language_server) => language_server, None => { warn!("can't find language server with id `{}`", server_id); diff --git a/helix-term/src/commands/lsp.rs b/helix-term/src/commands/lsp.rs index 38ba98d4..f7d35873 100644 --- a/helix-term/src/commands/lsp.rs +++ b/helix-term/src/commands/lsp.rs @@ -301,7 +301,7 @@ fn diag_picker( flat_diag.reserve(diags.len()); for (diag, ls) in diags { - if let Some(ls) = cx.editor.language_servers.get_by_id(ls) { + if let Some(ls) = cx.editor.language_server_by_id(ls) { flat_diag.push(PickerDiagnostic { url: url.clone(), diag, @@ -725,7 +725,7 @@ pub fn code_action(cx: &mut Context) { // always present here let action = action.unwrap(); - let Some(language_server) = editor.language_servers.get_by_id(action.language_server_id) else { + let Some(language_server) = editor.language_server_by_id(action.language_server_id) else { editor.set_error("Language Server disappeared"); return; }; @@ -772,8 +772,7 @@ pub fn execute_lsp_command(editor: &mut Editor, language_server_id: usize, cmd: // the command is executed on the server and communicated back // to the client asynchronously using workspace edits let future = match editor - .language_servers - .get_by_id(language_server_id) + .language_server_by_id(language_server_id) .and_then(|language_server| language_server.command(cmd)) { Some(future) => future, diff --git a/helix-term/src/ui/completion.rs b/helix-term/src/ui/completion.rs index e62efdac..28a5157c 100644 --- a/helix-term/src/ui/completion.rs +++ b/helix-term/src/ui/completion.rs @@ -212,6 +212,23 @@ impl Completion { let (view, doc) = current!(editor); + macro_rules! language_server { + ($item:expr) => { + match editor + .language_servers + .get_by_id($item.language_server_id) + { + Some(ls) => ls, + None => { + editor.set_error("language server disappeared between completion request and application"); + // TODO close the completion menu somehow, + // currently there is no trivial way to access the EditorView to close the completion menu + return; + } + } + }; + } + match event { PromptEvent::Abort => {} PromptEvent::Update => { @@ -236,17 +253,11 @@ impl Completion { // always present here let item = item.unwrap(); - let offset_encoding = editor - .language_servers - .get_by_id(item.language_server_id) - .expect("language server disappeared between completion request and application") - .offset_encoding(); - let transaction = item_to_transaction( doc, view.id, item, - offset_encoding, + language_server!(item).offset_encoding(), trigger_offset, true, replace_mode, @@ -262,11 +273,8 @@ impl Completion { // always present here let mut item = item.unwrap().clone(); - let offset_encoding = editor - .language_servers - .get_by_id(item.language_server_id) - .expect("language server disappeared between completion request and application") - .offset_encoding(); + let language_server = language_server!(item); + let offset_encoding = language_server.offset_encoding(); let language_server = editor .language_servers @@ -401,20 +409,11 @@ impl Completion { Some(item) if !item.resolved => item.clone(), _ => return false, }; - let language_server = match cx - .editor - .language_servers - .get_by_id(current_item.language_server_id) - { - Some(language_server) => language_server, - None => return false, - }; + + let Some(language_server) = cx.editor.language_server_by_id(current_item.language_server_id) else { return false; }; // This method should not block the compositor so we handle the response asynchronously. - let future = match language_server.resolve_completion_item(current_item.item.clone()) { - Some(future) => future, - None => return false, - }; + let Some(future) = language_server.resolve_completion_item(current_item.item.clone()) else { return false; }; cx.callback( future, diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index ca2144fd..afb8d91f 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -874,7 +874,7 @@ pub struct Editor { /// times during rendering and should not be set by other functions. pub cursor_cache: Cell>>, /// When a new completion request is sent to the server old - /// unifinished request must be dropped. Each completion + /// unfinished request must be dropped. Each completion /// request is associated with a channel that cancels /// when the channel is dropped. That channel is stored /// here. When a new completion request is sent this @@ -1093,6 +1093,11 @@ impl Editor { self._refresh(); } + #[inline] + pub fn language_server_by_id(&self, language_server_id: usize) -> Option<&helix_lsp::Client> { + self.language_servers.get_by_id(language_server_id) + } + /// Refreshes the language server for a given document pub fn refresh_language_servers(&mut self, doc_id: DocumentId) -> Option<()> { self.launch_language_servers(doc_id)