diff options
author | Blaž Hrastnik | 2023-05-19 00:39:35 +0000 |
---|---|---|
committer | GitHub | 2023-05-19 00:39:35 +0000 |
commit | 53f47bc47771c94dab51626ca025be28e62eba0c (patch) | |
tree | c8f5c59d40d1ecde227c209f898cc7afd6da5477 /helix-view/src/editor.rs | |
parent | 7f5940be80eaa3aec7903903072b7108f41dd97b (diff) | |
parent | 2a512f7c487f0a707a7eb158e24bd478433bcd91 (diff) |
Merge pull request #2507 from Philipp-M/multiple-language-servers
Add support for multiple language servers per language
Diffstat (limited to 'helix-view/src/editor.rs')
-rw-r--r-- | helix-view/src/editor.rs | 70 |
1 files changed, 43 insertions, 27 deletions
diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index 9546d460..1f27603c 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -818,7 +818,7 @@ pub struct Editor { pub macro_recording: Option<(char, Vec<KeyEvent>)>, pub macro_replaying: Vec<char>, pub language_servers: helix_lsp::Registry, - pub diagnostics: BTreeMap<lsp::Url, Vec<lsp::Diagnostic>>, + pub diagnostics: BTreeMap<lsp::Url, Vec<(lsp::Diagnostic, usize)>>, pub diff_providers: DiffProviderRegistry, pub debugger: Option<dap::Client>, @@ -874,7 +874,7 @@ pub struct Editor { /// times during rendering and should not be set by other functions. pub cursor_cache: Cell<Option<Option<Position>>>, /// 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 @@ -941,6 +941,7 @@ impl Editor { syn_loader: Arc<syntax::Loader>, config: Arc<dyn DynAccess<Config>>, ) -> Self { + let language_servers = helix_lsp::Registry::new(syn_loader.clone()); let conf = config.load(); let auto_pairs = (&conf.auto_pairs).into(); @@ -960,7 +961,7 @@ impl Editor { macro_recording: None, macro_replaying: Vec::new(), theme: theme_loader.default(), - language_servers: helix_lsp::Registry::new(), + language_servers, diagnostics: BTreeMap::new(), diff_providers: DiffProviderRegistry::default(), debugger: None, @@ -1092,60 +1093,75 @@ 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_server(&mut self, doc_id: DocumentId) -> Option<()> { - self.launch_language_server(doc_id) + pub fn refresh_language_servers(&mut self, doc_id: DocumentId) -> Option<()> { + self.launch_language_servers(doc_id) } /// Launch a language server for a given document - fn launch_language_server(&mut self, doc_id: DocumentId) -> Option<()> { + fn launch_language_servers(&mut self, doc_id: DocumentId) -> Option<()> { if !self.config().lsp.enable { return None; } - // if doc doesn't have a URL it's a scratch buffer, ignore it - let doc = self.document(doc_id)?; + let doc = self.documents.get_mut(&doc_id)?; + let doc_url = doc.url()?; let (lang, path) = (doc.language.clone(), doc.path().cloned()); let config = doc.config.load(); let root_dirs = &config.workspace_lsp_roots; - // try to find a language server based on the language name - let language_server = lang.as_ref().and_then(|language| { + // try to find language servers based on the language name + let language_servers = lang.as_ref().and_then(|language| { self.language_servers .get(language, path.as_ref(), root_dirs, config.lsp.snippets) .map_err(|e| { log::error!( - "Failed to initialize the LSP for `{}` {{ {} }}", + "Failed to initialize the language servers for `{}` {{ {} }}", language.scope(), e ) }) .ok() - .flatten() }); - let doc = self.document_mut(doc_id)?; - let doc_url = doc.url()?; + if let Some(language_servers) = language_servers { + let language_id = doc.language_id().map(ToOwned::to_owned).unwrap_or_default(); - if let Some(language_server) = language_server { - // only spawn a new lang server if the servers aren't the same - if Some(language_server.id()) != doc.language_server().map(|server| server.id()) { - if let Some(language_server) = doc.language_server() { - tokio::spawn(language_server.text_document_did_close(doc.identifier())); - } + // only spawn new language servers if the servers aren't the same - let language_id = doc.language_id().map(ToOwned::to_owned).unwrap_or_default(); + let doc_language_servers_not_in_registry = + doc.language_servers.iter().filter(|(name, doc_ls)| { + language_servers + .get(*name) + .map_or(true, |ls| ls.id() != doc_ls.id()) + }); + for (_, language_server) in doc_language_servers_not_in_registry { + tokio::spawn(language_server.text_document_did_close(doc.identifier())); + } + + let language_servers_not_in_doc = language_servers.iter().filter(|(name, ls)| { + doc.language_servers + .get(*name) + .map_or(true, |doc_ls| ls.id() != doc_ls.id()) + }); + + for (_, language_server) in language_servers_not_in_doc { // TODO: this now races with on_init code if the init happens too quickly tokio::spawn(language_server.text_document_did_open( - doc_url, + doc_url.clone(), doc.version(), doc.text(), - language_id, + language_id.clone(), )); - - doc.set_language_server(Some(language_server)); } + + doc.language_servers = language_servers; } Some(()) } @@ -1338,7 +1354,7 @@ impl Editor { doc.set_version_control_head(self.diff_providers.get_current_head_name(&path)); let id = self.new_document(doc); - let _ = self.launch_language_server(id); + let _ = self.launch_language_servers(id); id }; @@ -1368,7 +1384,7 @@ impl Editor { // This will also disallow any follow-up writes self.saves.remove(&doc_id); - if let Some(language_server) = doc.language_server() { + for language_server in doc.language_servers() { // TODO: track error tokio::spawn(language_server.text_document_did_close(doc.identifier())); } |