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/document.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/document.rs')
-rw-r--r-- | helix-view/src/document.rs | 112 |
1 files changed, 77 insertions, 35 deletions
diff --git a/helix-view/src/document.rs b/helix-view/src/document.rs index eb376567..bd3c465d 100644 --- a/helix-view/src/document.rs +++ b/helix-view/src/document.rs @@ -6,7 +6,7 @@ use futures_util::FutureExt; use helix_core::auto_pairs::AutoPairs; use helix_core::doc_formatter::TextFormat; use helix_core::encoding::Encoding; -use helix_core::syntax::Highlight; +use helix_core::syntax::{Highlight, LanguageServerFeature}; use helix_core::text_annotations::{InlineAnnotation, TextAnnotations}; use helix_core::Range; use helix_vcs::{DiffHandle, DiffProviderRegistry}; @@ -179,8 +179,8 @@ pub struct Document { version: i32, // should be usize? pub(crate) modified_since_accessed: bool, - diagnostics: Vec<Diagnostic>, - language_server: Option<Arc<helix_lsp::Client>>, + pub(crate) diagnostics: Vec<Diagnostic>, + pub(crate) language_servers: HashMap<LanguageServerName, Arc<Client>>, diff_handle: Option<DiffHandle>, version_control_head: Option<Arc<ArcSwap<Box<str>>>>, @@ -580,7 +580,7 @@ where *mut_ref = f(mem::take(mut_ref)); } -use helix_lsp::lsp; +use helix_lsp::{lsp, Client, LanguageServerName}; use url::Url; impl Document { @@ -616,7 +616,7 @@ impl Document { last_saved_time: SystemTime::now(), last_saved_revision: 0, modified_since_accessed: false, - language_server: None, + language_servers: HashMap::new(), diff_handle: None, config, version_control_head: None, @@ -730,10 +730,12 @@ impl Document { return Some(formatting_future.boxed()); }; - let language_server = self.language_server()?; let text = self.text.clone(); + // finds first language server that supports formatting and then formats + let language_server = self + .language_servers_with_feature(LanguageServerFeature::Format) + .next()?; let offset_encoding = language_server.offset_encoding(); - let request = language_server.text_document_formatting( self.identifier(), lsp::FormattingOptions { @@ -797,13 +799,12 @@ impl Document { if self.path.is_none() { bail!("Can't save with no path set!"); } - self.path.as_ref().unwrap().clone() } }; let identifier = self.path().map(|_| self.identifier()); - let language_server = self.language_server.clone(); + let language_servers = self.language_servers.clone(); // mark changes up to now as saved let current_rev = self.get_current_revision(); @@ -847,14 +848,13 @@ impl Document { text: text.clone(), }; - if let Some(language_server) = language_server { + for (_, language_server) in language_servers { if !language_server.is_initialized() { return Ok(event); } - - if let Some(identifier) = identifier { + if let Some(identifier) = &identifier { if let Some(notification) = - language_server.text_document_did_save(identifier, &text) + language_server.text_document_did_save(identifier.clone(), &text) { notification.await?; } @@ -1004,11 +1004,6 @@ impl Document { Ok(()) } - /// Set the LSP. - pub fn set_language_server(&mut self, language_server: Option<Arc<helix_lsp::Client>>) { - self.language_server = language_server; - } - /// Select text within the [`Document`]. pub fn set_selection(&mut self, view_id: ViewId, selection: Selection) { // TODO: use a transaction? @@ -1159,7 +1154,7 @@ impl Document { if emit_lsp_notification { // emit lsp notification - if let Some(language_server) = self.language_server() { + for language_server in self.language_servers() { let notify = language_server.text_document_did_change( self.versioned_identifier(), &old_doc, @@ -1415,18 +1410,13 @@ impl Document { .map(|language| language.language_id.as_str()) } - /// Language ID for the document. Either the `language-id` from the - /// `language-server` configuration, or the document language if no - /// `language-id` has been specified. + /// Language ID for the document. Either the `language-id`, + /// or the document language name if no `language-id` has been specified. pub fn language_id(&self) -> Option<&str> { - let language_config = self.language.as_deref()?; - - language_config - .language_server - .as_ref()? - .language_id + self.language_config()? + .language_server_language_id .as_deref() - .or(Some(language_config.language_id.as_str())) + .or_else(|| self.language_name()) } /// Corresponding [`LanguageConfiguration`]. @@ -1439,10 +1429,45 @@ impl Document { self.version } - /// Language server if it has been initialized. - pub fn language_server(&self) -> Option<&helix_lsp::Client> { - let server = self.language_server.as_deref()?; - server.is_initialized().then_some(server) + /// maintains the order as configured in the language_servers TOML array + pub fn language_servers(&self) -> impl Iterator<Item = &helix_lsp::Client> { + self.language_config().into_iter().flat_map(move |config| { + config.language_servers.iter().filter_map(move |features| { + let ls = &**self.language_servers.get(&features.name)?; + if ls.is_initialized() { + Some(ls) + } else { + None + } + }) + }) + } + + pub fn remove_language_server_by_name(&mut self, name: &str) -> Option<Arc<Client>> { + self.language_servers.remove(name) + } + + pub fn language_servers_with_feature( + &self, + feature: LanguageServerFeature, + ) -> impl Iterator<Item = &helix_lsp::Client> { + self.language_config().into_iter().flat_map(move |config| { + config.language_servers.iter().filter_map(move |features| { + let ls = &**self.language_servers.get(&features.name)?; + if ls.is_initialized() + && ls.supports_feature(feature) + && features.has_feature(feature) + { + Some(ls) + } else { + None + } + }) + }) + } + + pub fn supports_language_server(&self, id: usize) -> bool { + self.language_servers().any(|l| l.id() == id) } pub fn diff_handle(&self) -> Option<&DiffHandle> { @@ -1565,12 +1590,29 @@ impl Document { &self.diagnostics } - pub fn set_diagnostics(&mut self, diagnostics: Vec<Diagnostic>) { - self.diagnostics = diagnostics; + pub fn shown_diagnostics(&self) -> impl Iterator<Item = &Diagnostic> + DoubleEndedIterator { + self.diagnostics.iter().filter(|d| { + self.language_servers_with_feature(LanguageServerFeature::Diagnostics) + .any(|ls| ls.id() == d.language_server_id) + }) + } + + pub fn replace_diagnostics( + &mut self, + mut diagnostics: Vec<Diagnostic>, + language_server_id: usize, + ) { + self.clear_diagnostics(language_server_id); + self.diagnostics.append(&mut diagnostics); self.diagnostics .sort_unstable_by_key(|diagnostic| diagnostic.range); } + pub fn clear_diagnostics(&mut self, language_server_id: usize) { + self.diagnostics + .retain(|d| d.language_server_id != language_server_id); + } + /// Get the document's auto pairs. If the document has a recognized /// language config with auto pairs configured, returns that; /// otherwise, falls back to the global auto pairs config. If the global |