on master help sign in
Refactor doc language servers to a HashMap, and the config to use a Vec to retain order
2eeac10Philipp Mildenberger
helix-core/src/syntax.rs+17−17
@@ -112,11 +112,11 @@ pub struct LanguageConfiguration {
112 // tags_config OnceCell<> https://github.com/tree-sitter/tree-sitter/pull/583
112 // tags_config OnceCell<> https://github.com/tree-sitter/tree-sitter/pull/583
113 #[serde(
113 #[serde(
114 default,
114 default,
115− skip_serializing_if = "HashMap::is_empty",
115+ skip_serializing_if = "Vec::is_empty",
116 serialize_with = "serialize_lang_features",
116 serialize_with = "serialize_lang_features",
117 deserialize_with = "deserialize_lang_features"
117 deserialize_with = "deserialize_lang_features"
118 )]
118 )]
119− pub language_servers: HashMap<String, LanguageServerFeatures>,
119+ pub language_servers: Vec<LanguageServerFeatures>,
120 #[serde(skip_serializing_if = "Option::is_none")]
120 #[serde(skip_serializing_if = "Option::is_none")]
121 pub indent: Option<IndentationConfiguration>,
121 pub indent: Option<IndentationConfiguration>,
122
122
@@ -282,19 +282,20 @@ enum LanguageServerFeatureConfiguration {
282
282
283#[derive(Debug, Default)]
283#[derive(Debug, Default)]
284pub struct LanguageServerFeatures {
284pub struct LanguageServerFeatures {
285+ pub name: String,
285 pub only: HashSet<LanguageServerFeature>,
286 pub only: HashSet<LanguageServerFeature>,
286 pub excluded: HashSet<LanguageServerFeature>,
287 pub excluded: HashSet<LanguageServerFeature>,
287}
288}
288
289
289impl LanguageServerFeatures {
290impl LanguageServerFeatures {
290 pub fn has_feature(&self, feature: LanguageServerFeature) -> bool {
291 pub fn has_feature(&self, feature: LanguageServerFeature) -> bool {
291− self.only.is_empty() || self.only.contains(&feature) && !self.excluded.contains(&feature)
292+ (self.only.is_empty() || self.only.contains(&feature)) && !self.excluded.contains(&feature)
292 }
293 }
293}
294}
294
295
295fn deserialize_lang_features<'de, D>(
296fn deserialize_lang_features<'de, D>(
296 deserializer: D,
297 deserializer: D,
297−) -> Result<HashMap<String, LanguageServerFeatures>, D::Error>
298+) -> Result<Vec<LanguageServerFeatures>, D::Error>
298where
299where
299 D: serde::Deserializer<'de>,
300 D: serde::Deserializer<'de>,
300{
301{
@@ -302,40 +303,39 @@ where
302 let res = raw
303 let res = raw
303 .into_iter()
304 .into_iter()
304 .map(|config| match config {
305 .map(|config| match config {
305− LanguageServerFeatureConfiguration::Simple(name) => {
306+ LanguageServerFeatureConfiguration::Simple(name) => LanguageServerFeatures {
306− (name, LanguageServerFeatures::default())
307+ name,
308+ ..Default::default()
307− }
309+ },
308 LanguageServerFeatureConfiguration::Features {
310 LanguageServerFeatureConfiguration::Features {
309 only_features,
311 only_features,
310 except_features,
312 except_features,
311 name,
313 name,
312− } => (
314+ } => LanguageServerFeatures {
313 name,
315 name,
314− LanguageServerFeatures {
315− only: only_features,
316+ only: only_features,
316− excluded: except_features,
317+ excluded: except_features,
317− },
318− ),
318+ },
319 })
319 })
320 .collect();
320 .collect();
321 Ok(res)
321 Ok(res)
322}
322}
323fn serialize_lang_features<S>(
323fn serialize_lang_features<S>(
324− map: &HashMap<String, LanguageServerFeatures>,
324+ map: &Vec<LanguageServerFeatures>,
325 serializer: S,
325 serializer: S,
326) -> Result<S::Ok, S::Error>
326) -> Result<S::Ok, S::Error>
327where
327where
328 S: serde::Serializer,
328 S: serde::Serializer,
329{
329{
330 let mut serializer = serializer.serialize_seq(Some(map.len()))?;
330 let mut serializer = serializer.serialize_seq(Some(map.len()))?;
331− for (name, features) in map {
331+ for features in map {
332 let features = if features.only.is_empty() && features.excluded.is_empty() {
332 let features = if features.only.is_empty() && features.excluded.is_empty() {
333− LanguageServerFeatureConfiguration::Simple(name.to_owned())
333+ LanguageServerFeatureConfiguration::Simple(features.name.to_owned())
334 } else {
334 } else {
335 LanguageServerFeatureConfiguration::Features {
335 LanguageServerFeatureConfiguration::Features {
336 only_features: features.only.clone(),
336 only_features: features.only.clone(),
337 except_features: features.excluded.clone(),
337 except_features: features.excluded.clone(),
338− name: name.to_owned(),
338+ name: features.name.to_owned(),
339 }
339 }
340 };
340 };
341 serializer.serialize_element(&features)?;
341 serializer.serialize_element(&features)?;
helix-lsp/src/lib.rs+13−10
@@ -12,7 +12,7 @@ pub use lsp_types as lsp;
12use futures_util::stream::select_all::SelectAll;
12use futures_util::stream::select_all::SelectAll;
13use helix_core::{
13use helix_core::{
14 path,
14 path,
15− syntax::{LanguageConfiguration, LanguageServerConfiguration},
15+ syntax::{LanguageConfiguration, LanguageServerConfiguration, LanguageServerFeatures},
16};
16};
17use tokio::sync::mpsc::UnboundedReceiver;
17use tokio::sync::mpsc::UnboundedReceiver;
18
18
@@ -26,7 +26,7 @@ use thiserror::Error;
26use tokio_stream::wrappers::UnboundedReceiverStream;
26use tokio_stream::wrappers::UnboundedReceiverStream;
27
27
28pub type Result<T> = core::result::Result<T, Error>;
28pub type Result<T> = core::result::Result<T, Error>;
29−type LanguageServerName = String;
29+pub type LanguageServerName = String;
30
30
31#[derive(Error, Debug)]
31#[derive(Error, Debug)]
32pub enum Error {
32pub enum Error {
@@ -689,9 +689,9 @@ impl Registry {
689 ) -> Result<Vec<Arc<Client>>> {
689 ) -> Result<Vec<Arc<Client>>> {
690 language_config
690 language_config
691 .language_servers
691 .language_servers
692− .keys()
692+ .iter()
693− .filter_map(|name| {
693+ .filter_map(|LanguageServerFeatures { name, .. }| {
694− #[allow(clippy::map_entry)]
694+ // #[allow(clippy::map_entry)]
695 if self.inner.contains_key(name) {
695 if self.inner.contains_key(name) {
696 let client = match self.start_client(
696 let client = match self.start_client(
697 name.clone(),
697 name.clone(),
@@ -740,17 +740,20 @@ impl Registry {
740 doc_path: Option<&std::path::PathBuf>,
740 doc_path: Option<&std::path::PathBuf>,
741 root_dirs: &[PathBuf],
741 root_dirs: &[PathBuf],
742 enable_snippets: bool,
742 enable_snippets: bool,
743− ) -> Result<Vec<Arc<Client>>> {
743+ ) -> Result<HashMap<LanguageServerName, Arc<Client>>> {
744 language_config
744 language_config
745 .language_servers
745 .language_servers
746− .keys()
746+ .iter()
747− .map(|name| {
747+ .map(|LanguageServerFeatures { name, .. }| {
748 if let Some(clients) = self.inner.get_mut(name) {
748 if let Some(clients) = self.inner.get_mut(name) {
749+ // clients.find(
750+
749 if let Some((_, client)) = clients.iter_mut().enumerate().find(|(i, client)| {
751 if let Some((_, client)) = clients.iter_mut().enumerate().find(|(i, client)| {
750 client.try_add_doc(&language_config.roots, root_dirs, doc_path, *i == 0)
752 client.try_add_doc(&language_config.roots, root_dirs, doc_path, *i == 0)
751 }) {
753 }) {
752− return Ok(client.clone());
754+ return Ok((name.to_owned(), client.clone()));
753 }
755 }
756+ // return Ok((name.clone(), clients.clone()));
754 }
757 }
755 let client = self.start_client(
758 let client = self.start_client(
756 name.clone(),
759 name.clone(),
@@ -761,7 +764,7 @@ impl Registry {
761 )?;
764 )?;
762 let clients = self.inner.entry(name.clone()).or_default();
765 let clients = self.inner.entry(name.clone()).or_default();
763 clients.push(client.clone());
766 clients.push(client.clone());
764− Ok(client)
767+ Ok((name.clone(), client))
765 })
768 })
766 .collect()
769 .collect()
767 }
770 }
helix-term/src/health.rs+4−4
@@ -194,10 +194,10 @@ pub fn languages_all() -> std::io::Result<()> {
194
194
195 // TODO multiple language servers (check binary for each supported language server, not just the first)
195 // TODO multiple language servers (check binary for each supported language server, not just the first)
196
196
197− let lsp = lang.language_servers.keys().next().and_then(|ls_name| {
197+ let lsp = lang.language_servers.first().and_then(|ls| {
198 syn_loader_conf
198 syn_loader_conf
199 .language_server
199 .language_server
200− .get(ls_name)
200+ .get(&ls.name)
201 .map(|config| config.command.clone())
201 .map(|config| config.command.clone())
202 });
202 });
203 check_binary(lsp);
203 check_binary(lsp);
@@ -271,10 +271,10 @@ pub fn language(lang_str: String) -> std::io::Result<()> {
271 // TODO multiple language servers
271 // TODO multiple language servers
272 probe_protocol(
272 probe_protocol(
273 "language server",
273 "language server",
274− lang.language_servers.keys().next().and_then(|ls_name| {
274+ lang.language_servers.first().and_then(|ls| {
275 syn_loader_conf
275 syn_loader_conf
276 .language_server
276 .language_server
277− .get(ls_name)
277+ .get(&ls.name)
278 .map(|config| config.command.clone())
278 .map(|config| config.command.clone())
279 }),
279 }),
280 )?;
280 )?;
helix-view/src/document.rs+25−23
@@ -180,7 +180,7 @@ pub struct Document {
180 pub(crate) modified_since_accessed: bool,
180 pub(crate) modified_since_accessed: bool,
181
181
182 diagnostics: Vec<Diagnostic>,
182 diagnostics: Vec<Diagnostic>,
183− language_servers: Vec<Arc<helix_lsp::Client>>,
183+ pub(crate) language_servers: HashMap<LanguageServerName, Arc<Client>>,
184
184
185 diff_handle: Option<DiffHandle>,
185 diff_handle: Option<DiffHandle>,
186 version_control_head: Option<Arc<ArcSwap<Box<str>>>>,
186 version_control_head: Option<Arc<ArcSwap<Box<str>>>>,
@@ -580,7 +580,7 @@ where
580 *mut_ref = f(mem::take(mut_ref));
580 *mut_ref = f(mem::take(mut_ref));
581}
581}
582
582
583−use helix_lsp::{lsp, Client, OffsetEncoding};
583+use helix_lsp::{lsp, Client, LanguageServerName, OffsetEncoding};
584use url::Url;
584use url::Url;
585
585
586impl Document {
586impl Document {
@@ -616,7 +616,7 @@ impl Document {
616 last_saved_time: SystemTime::now(),
616 last_saved_time: SystemTime::now(),
617 last_saved_revision: 0,
617 last_saved_revision: 0,
618 modified_since_accessed: false,
618 modified_since_accessed: false,
619− language_servers: Vec::new(),
619+ language_servers: HashMap::new(),
620 diff_handle: None,
620 diff_handle: None,
621 config,
621 config,
622 version_control_head: None,
622 version_control_head: None,
@@ -850,7 +850,7 @@ impl Document {
850 text: text.clone(),
850 text: text.clone(),
851 };
851 };
852
852
853− for language_server in language_servers {
853+ for (_, language_server) in language_servers {
854 if !language_server.is_initialized() {
854 if !language_server.is_initialized() {
855 return Ok(event);
855 return Ok(event);
856 }
856 }
@@ -1006,11 +1006,6 @@ impl Document {
1006 Ok(())
1006 Ok(())
1007 }
1007 }
1008
1008
1009− /// Set the LSP.
1010− pub fn set_language_servers(&mut self, language_servers: Vec<Arc<helix_lsp::Client>>) {
1011− self.language_servers = language_servers;
1012− }
1013−
1014 /// Select text within the [`Document`].
1009 /// Select text within the [`Document`].
1015 pub fn set_selection(&mut self, view_id: ViewId, selection: Selection) {
1010 pub fn set_selection(&mut self, view_id: ViewId, selection: Selection) {
1016 // TODO: use a transaction?
1011 // TODO: use a transaction?
@@ -1437,16 +1432,17 @@ impl Document {
1437 }
1432 }
1438
1433
1439 pub fn language_servers(&self) -> impl Iterator<Item = &helix_lsp::Client> {
1434 pub fn language_servers(&self) -> impl Iterator<Item = &helix_lsp::Client> {
1440− self.language_servers
1435+ self.language_servers.values().filter_map(|l| {
1441− .iter()
1442− .filter_map(|l| if l.is_initialized() { Some(&**l) } else { None })
1436+ if l.is_initialized() {
1437+ Some(&**l)
1438+ } else {
1439+ None
1440+ }
1441+ })
1443 }
1442 }
1444
1443
1445 pub fn remove_language_server_by_name(&mut self, name: &str) -> Option<Arc<Client>> {
1444 pub fn remove_language_server_by_name(&mut self, name: &str) -> Option<Arc<Client>> {
1446− match self.language_servers.iter().position(|l| l.name() == name) {
1447− Some(index) => Some(self.language_servers.remove(index)),
1445+ self.language_servers.remove(name)
1448− None => None,
1449− }
1450 }
1446 }
1451
1447
1452 // TODO filter also based on LSP capabilities?
1448 // TODO filter also based on LSP capabilities?
@@ -1454,12 +1450,15 @@ impl Document {
1454 &self,
1450 &self,
1455 feature: LanguageServerFeature,
1451 feature: LanguageServerFeature,
1456 ) -> impl Iterator<Item = &helix_lsp::Client> {
1452 ) -> impl Iterator<Item = &helix_lsp::Client> {
1457− self.language_servers().filter(move |server| {
1458− self.language_config()
1453+ self.language_config().into_iter().flat_map(move |config| {
1459− .and_then(|config| config.language_servers.get(server.name()))
1454+ config.language_servers.iter().filter_map(move |features| {
1460− .map_or(false, |server_features| {
1455+ let ls = &**self.language_servers.get(&features.name)?;
1461− server_features.has_feature(feature)
1456+ if ls.is_initialized() && features.has_feature(feature) {
1457+ Some(ls)
1458+ } else {
1459+ None
1460+ }
1462− })
1461+ })
1463 })
1462 })
1464 }
1463 }
1465
1464
@@ -1610,7 +1609,10 @@ impl Document {
1610 .find(|ls| ls.id() == d.language_server_id)
1609 .find(|ls| ls.id() == d.language_server_id)
1611 .and_then(|ls| {
1610 .and_then(|ls| {
1612 let config = self.language_config()?;
1611 let config = self.language_config()?;
1613− let features = config.language_servers.get(ls.name())?;
1612+ let features = config
1613+ .language_servers
1614+ .iter()
1615+ .find(|features| features.name == ls.name())?;
1614 Some(features.has_feature(LanguageServerFeature::Diagnostics))
1616 Some(features.has_feature(LanguageServerFeature::Diagnostics))
1615 })
1617 })
1616 == Some(true)
1618 == Some(true)
helix-view/src/editor.rs+29−29
@@ -689,7 +689,7 @@ pub struct WhitespaceCharacters {
689impl Default for WhitespaceCharacters {
689impl Default for WhitespaceCharacters {
690 fn default() -> Self {
690 fn default() -> Self {
691 Self {
691 Self {
692− space: '·', // U+00B7
692+ space: '·', // U+00B7
693 nbsp: '⍽', // U+237D
693 nbsp: '⍽', // U+237D
694 tab: '→', // U+2192
694 tab: '→', // U+2192
695 newline: '⏎', // U+23CE
695 newline: '⏎', // U+23CE
@@ -1103,9 +1103,9 @@ impl Editor {
1103 if !self.config().lsp.enable {
1103 if !self.config().lsp.enable {
1104 return None;
1104 return None;
1105 }
1105 }
1106−
1107 // if doc doesn't have a URL it's a scratch buffer, ignore it
1106 // if doc doesn't have a URL it's a scratch buffer, ignore it
1108− let doc = self.document(doc_id)?;
1107+ let doc = self.documents.get_mut(&doc_id)?;
1108+ let doc_url = doc.url()?;
1109 let (lang, path) = (doc.language.clone(), doc.path().cloned());
1109 let (lang, path) = (doc.language.clone(), doc.path().cloned());
1110 let config = doc.config.load();
1110 let config = doc.config.load();
1111 let root_dirs = &config.workspace_lsp_roots;
1111 let root_dirs = &config.workspace_lsp_roots;
@@ -1124,37 +1124,37 @@ impl Editor {
1124 .ok()
1124 .ok()
1125 });
1125 });
1126
1126
1127− let doc = self.document_mut(doc_id)?;
1128− let doc_url = doc.url()?;
1129−
1130 if let Some(language_servers) = language_servers {
1127 if let Some(language_servers) = language_servers {
1131− // only spawn new lang servers if the servers aren't the same
1128+ let language_id = doc.language_id().map(ToOwned::to_owned).unwrap_or_default();
1132− // TODO simplify?
1133− let doc_language_servers = doc.language_servers().collect::<Vec<_>>();
1134− let spawn_new_servers = language_servers.len() != doc_language_servers.len()
1135− || language_servers
1136− .iter()
1137− .zip(doc_language_servers.iter())
1138− .any(|(l, dl)| l.id() != dl.id());
1139− if spawn_new_servers {
1140− for doc_language_server in doc_language_servers {
1141− tokio::spawn(doc_language_server.text_document_did_close(doc.identifier()));
1142− }
1143
1129
1144− let language_id = doc.language_id().map(ToOwned::to_owned).unwrap_or_default();
1130+ // only spawn new language servers if the servers aren't the same
1145
1131
1132+ let doc_language_servers_not_in_registry =
1146− for language_server in &language_servers {
1133+ doc.language_servers.iter().filter(|(name, doc_ls)| {
1147− // TODO: this now races with on_init code if the init happens too quickly
1134+ !language_servers.contains_key(*name)
1148− tokio::spawn(language_server.text_document_did_open(
1135+ || language_servers[*name].id() != doc_ls.id()
1149− doc_url.clone(),
1150− doc.version(),
1151− doc.text(),
1152− language_id.clone(),
1153− ));
1136+ });
1154− }
1155
1137
1156− doc.set_language_servers(language_servers);
1138+ for (_, language_server) in doc_language_servers_not_in_registry {
1139+ tokio::spawn(language_server.text_document_did_close(doc.identifier()));
1157 }
1140 }
1141+
1142+ let language_servers_not_in_doc = language_servers.iter().filter(|(name, ls)| {
1143+ !doc.language_servers.contains_key(*name)
1144+ || doc.language_servers[*name].id() != ls.id()
1145+ });
1146+
1147+ for (_, language_server) in language_servers_not_in_doc {
1148+ // TODO: this now races with on_init code if the init happens too quickly
1149+ tokio::spawn(language_server.text_document_did_open(
1150+ doc_url.clone(),
1151+ doc.version(),
1152+ doc.text(),
1153+ language_id.clone(),
1154+ ));
1155+ }
1156+
1157+ doc.language_servers = language_servers;
1158 }
1158 }
1159 Some(())
1159 Some(())
1160 }
1160 }