Refactor doc language servers to a HashMap, and the config to use a Vec to retain order

pull/2507/head
Philipp Mildenberger 2 years ago
parent b1199c552b
commit 2eeac10755

@ -112,11 +112,11 @@ pub struct LanguageConfiguration {
// tags_config OnceCell<> https://github.com/tree-sitter/tree-sitter/pull/583 // tags_config OnceCell<> https://github.com/tree-sitter/tree-sitter/pull/583
#[serde( #[serde(
default, default,
skip_serializing_if = "HashMap::is_empty", skip_serializing_if = "Vec::is_empty",
serialize_with = "serialize_lang_features", serialize_with = "serialize_lang_features",
deserialize_with = "deserialize_lang_features" deserialize_with = "deserialize_lang_features"
)] )]
pub language_servers: HashMap<String, LanguageServerFeatures>, pub language_servers: Vec<LanguageServerFeatures>,
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub indent: Option<IndentationConfiguration>, pub indent: Option<IndentationConfiguration>,
@ -282,19 +282,20 @@ enum LanguageServerFeatureConfiguration {
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct LanguageServerFeatures { pub struct LanguageServerFeatures {
pub name: String,
pub only: HashSet<LanguageServerFeature>, pub only: HashSet<LanguageServerFeature>,
pub excluded: HashSet<LanguageServerFeature>, pub excluded: HashSet<LanguageServerFeature>,
} }
impl LanguageServerFeatures { impl LanguageServerFeatures {
pub fn has_feature(&self, feature: LanguageServerFeature) -> bool { pub fn has_feature(&self, feature: LanguageServerFeature) -> bool {
self.only.is_empty() || self.only.contains(&feature) && !self.excluded.contains(&feature) (self.only.is_empty() || self.only.contains(&feature)) && !self.excluded.contains(&feature)
} }
} }
fn deserialize_lang_features<'de, D>( fn deserialize_lang_features<'de, D>(
deserializer: D, deserializer: D,
) -> Result<HashMap<String, LanguageServerFeatures>, D::Error> ) -> Result<Vec<LanguageServerFeatures>, D::Error>
where where
D: serde::Deserializer<'de>, D: serde::Deserializer<'de>,
{ {
@ -302,40 +303,39 @@ where
let res = raw let res = raw
.into_iter() .into_iter()
.map(|config| match config { .map(|config| match config {
LanguageServerFeatureConfiguration::Simple(name) => { LanguageServerFeatureConfiguration::Simple(name) => LanguageServerFeatures {
(name, LanguageServerFeatures::default()) name,
} ..Default::default()
},
LanguageServerFeatureConfiguration::Features { LanguageServerFeatureConfiguration::Features {
only_features, only_features,
except_features, except_features,
name, name,
} => ( } => LanguageServerFeatures {
name, name,
LanguageServerFeatures {
only: only_features, only: only_features,
excluded: except_features, excluded: except_features,
}, },
),
}) })
.collect(); .collect();
Ok(res) Ok(res)
} }
fn serialize_lang_features<S>( fn serialize_lang_features<S>(
map: &HashMap<String, LanguageServerFeatures>, map: &Vec<LanguageServerFeatures>,
serializer: S, serializer: S,
) -> Result<S::Ok, S::Error> ) -> Result<S::Ok, S::Error>
where where
S: serde::Serializer, S: serde::Serializer,
{ {
let mut serializer = serializer.serialize_seq(Some(map.len()))?; let mut serializer = serializer.serialize_seq(Some(map.len()))?;
for (name, features) in map { for features in map {
let features = if features.only.is_empty() && features.excluded.is_empty() { let features = if features.only.is_empty() && features.excluded.is_empty() {
LanguageServerFeatureConfiguration::Simple(name.to_owned()) LanguageServerFeatureConfiguration::Simple(features.name.to_owned())
} else { } else {
LanguageServerFeatureConfiguration::Features { LanguageServerFeatureConfiguration::Features {
only_features: features.only.clone(), only_features: features.only.clone(),
except_features: features.excluded.clone(), except_features: features.excluded.clone(),
name: name.to_owned(), name: features.name.to_owned(),
} }
}; };
serializer.serialize_element(&features)?; serializer.serialize_element(&features)?;

@ -12,7 +12,7 @@ pub use lsp_types as lsp;
use futures_util::stream::select_all::SelectAll; use futures_util::stream::select_all::SelectAll;
use helix_core::{ use helix_core::{
path, path,
syntax::{LanguageConfiguration, LanguageServerConfiguration}, syntax::{LanguageConfiguration, LanguageServerConfiguration, LanguageServerFeatures},
}; };
use tokio::sync::mpsc::UnboundedReceiver; use tokio::sync::mpsc::UnboundedReceiver;
@ -26,7 +26,7 @@ use thiserror::Error;
use tokio_stream::wrappers::UnboundedReceiverStream; use tokio_stream::wrappers::UnboundedReceiverStream;
pub type Result<T> = core::result::Result<T, Error>; pub type Result<T> = core::result::Result<T, Error>;
type LanguageServerName = String; pub type LanguageServerName = String;
#[derive(Error, Debug)] #[derive(Error, Debug)]
pub enum Error { pub enum Error {
@ -689,9 +689,9 @@ impl Registry {
) -> Result<Vec<Arc<Client>>> { ) -> Result<Vec<Arc<Client>>> {
language_config language_config
.language_servers .language_servers
.keys() .iter()
.filter_map(|name| { .filter_map(|LanguageServerFeatures { name, .. }| {
#[allow(clippy::map_entry)] // #[allow(clippy::map_entry)]
if self.inner.contains_key(name) { if self.inner.contains_key(name) {
let client = match self.start_client( let client = match self.start_client(
name.clone(), name.clone(),
@ -740,17 +740,20 @@ impl Registry {
doc_path: Option<&std::path::PathBuf>, doc_path: Option<&std::path::PathBuf>,
root_dirs: &[PathBuf], root_dirs: &[PathBuf],
enable_snippets: bool, enable_snippets: bool,
) -> Result<Vec<Arc<Client>>> { ) -> Result<HashMap<LanguageServerName, Arc<Client>>> {
language_config language_config
.language_servers .language_servers
.keys() .iter()
.map(|name| { .map(|LanguageServerFeatures { name, .. }| {
if let Some(clients) = self.inner.get_mut(name) { if let Some(clients) = self.inner.get_mut(name) {
// clients.find(
if let Some((_, client)) = clients.iter_mut().enumerate().find(|(i, client)| { if let Some((_, client)) = clients.iter_mut().enumerate().find(|(i, client)| {
client.try_add_doc(&language_config.roots, root_dirs, doc_path, *i == 0) client.try_add_doc(&language_config.roots, root_dirs, doc_path, *i == 0)
}) { }) {
return Ok(client.clone()); return Ok((name.to_owned(), client.clone()));
} }
// return Ok((name.clone(), clients.clone()));
} }
let client = self.start_client( let client = self.start_client(
name.clone(), name.clone(),
@ -761,7 +764,7 @@ impl Registry {
)?; )?;
let clients = self.inner.entry(name.clone()).or_default(); let clients = self.inner.entry(name.clone()).or_default();
clients.push(client.clone()); clients.push(client.clone());
Ok(client) Ok((name.clone(), client))
}) })
.collect() .collect()
} }

@ -194,10 +194,10 @@ pub fn languages_all() -> std::io::Result<()> {
// TODO multiple language servers (check binary for each supported language server, not just the first) // TODO multiple language servers (check binary for each supported language server, not just the first)
let lsp = lang.language_servers.keys().next().and_then(|ls_name| { let lsp = lang.language_servers.first().and_then(|ls| {
syn_loader_conf syn_loader_conf
.language_server .language_server
.get(ls_name) .get(&ls.name)
.map(|config| config.command.clone()) .map(|config| config.command.clone())
}); });
check_binary(lsp); check_binary(lsp);
@ -271,10 +271,10 @@ pub fn language(lang_str: String) -> std::io::Result<()> {
// TODO multiple language servers // TODO multiple language servers
probe_protocol( probe_protocol(
"language server", "language server",
lang.language_servers.keys().next().and_then(|ls_name| { lang.language_servers.first().and_then(|ls| {
syn_loader_conf syn_loader_conf
.language_server .language_server
.get(ls_name) .get(&ls.name)
.map(|config| config.command.clone()) .map(|config| config.command.clone())
}), }),
)?; )?;

@ -180,7 +180,7 @@ pub struct Document {
pub(crate) modified_since_accessed: bool, pub(crate) modified_since_accessed: bool,
diagnostics: Vec<Diagnostic>, diagnostics: Vec<Diagnostic>,
language_servers: Vec<Arc<helix_lsp::Client>>, pub(crate) language_servers: HashMap<LanguageServerName, Arc<Client>>,
diff_handle: Option<DiffHandle>, diff_handle: Option<DiffHandle>,
version_control_head: Option<Arc<ArcSwap<Box<str>>>>, version_control_head: Option<Arc<ArcSwap<Box<str>>>>,
@ -580,7 +580,7 @@ where
*mut_ref = f(mem::take(mut_ref)); *mut_ref = f(mem::take(mut_ref));
} }
use helix_lsp::{lsp, Client, OffsetEncoding}; use helix_lsp::{lsp, Client, LanguageServerName, OffsetEncoding};
use url::Url; use url::Url;
impl Document { impl Document {
@ -616,7 +616,7 @@ impl Document {
last_saved_time: SystemTime::now(), last_saved_time: SystemTime::now(),
last_saved_revision: 0, last_saved_revision: 0,
modified_since_accessed: false, modified_since_accessed: false,
language_servers: Vec::new(), language_servers: HashMap::new(),
diff_handle: None, diff_handle: None,
config, config,
version_control_head: None, version_control_head: None,
@ -850,7 +850,7 @@ impl Document {
text: text.clone(), text: text.clone(),
}; };
for language_server in language_servers { for (_, language_server) in language_servers {
if !language_server.is_initialized() { if !language_server.is_initialized() {
return Ok(event); return Ok(event);
} }
@ -1006,11 +1006,6 @@ impl Document {
Ok(()) Ok(())
} }
/// Set the LSP.
pub fn set_language_servers(&mut self, language_servers: Vec<Arc<helix_lsp::Client>>) {
self.language_servers = language_servers;
}
/// Select text within the [`Document`]. /// Select text within the [`Document`].
pub fn set_selection(&mut self, view_id: ViewId, selection: Selection) { pub fn set_selection(&mut self, view_id: ViewId, selection: Selection) {
// TODO: use a transaction? // TODO: use a transaction?
@ -1437,16 +1432,17 @@ impl Document {
} }
pub fn language_servers(&self) -> impl Iterator<Item = &helix_lsp::Client> { pub fn language_servers(&self) -> impl Iterator<Item = &helix_lsp::Client> {
self.language_servers self.language_servers.values().filter_map(|l| {
.iter() if l.is_initialized() {
.filter_map(|l| if l.is_initialized() { Some(&**l) } else { None }) Some(&**l)
} else {
None
}
})
} }
pub fn remove_language_server_by_name(&mut self, name: &str) -> Option<Arc<Client>> { pub fn remove_language_server_by_name(&mut self, name: &str) -> Option<Arc<Client>> {
match self.language_servers.iter().position(|l| l.name() == name) { self.language_servers.remove(name)
Some(index) => Some(self.language_servers.remove(index)),
None => None,
}
} }
// TODO filter also based on LSP capabilities? // TODO filter also based on LSP capabilities?
@ -1454,11 +1450,14 @@ impl Document {
&self, &self,
feature: LanguageServerFeature, feature: LanguageServerFeature,
) -> impl Iterator<Item = &helix_lsp::Client> { ) -> impl Iterator<Item = &helix_lsp::Client> {
self.language_servers().filter(move |server| { self.language_config().into_iter().flat_map(move |config| {
self.language_config() config.language_servers.iter().filter_map(move |features| {
.and_then(|config| config.language_servers.get(server.name())) let ls = &**self.language_servers.get(&features.name)?;
.map_or(false, |server_features| { if ls.is_initialized() && features.has_feature(feature) {
server_features.has_feature(feature) Some(ls)
} else {
None
}
}) })
}) })
} }
@ -1610,7 +1609,10 @@ impl Document {
.find(|ls| ls.id() == d.language_server_id) .find(|ls| ls.id() == d.language_server_id)
.and_then(|ls| { .and_then(|ls| {
let config = self.language_config()?; let config = self.language_config()?;
let features = config.language_servers.get(ls.name())?; let features = config
.language_servers
.iter()
.find(|features| features.name == ls.name())?;
Some(features.has_feature(LanguageServerFeature::Diagnostics)) Some(features.has_feature(LanguageServerFeature::Diagnostics))
}) })
== Some(true) == Some(true)

@ -1103,9 +1103,9 @@ impl Editor {
if !self.config().lsp.enable { if !self.config().lsp.enable {
return None; return None;
} }
// if doc doesn't have a URL it's a scratch buffer, ignore it // 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 (lang, path) = (doc.language.clone(), doc.path().cloned());
let config = doc.config.load(); let config = doc.config.load();
let root_dirs = &config.workspace_lsp_roots; let root_dirs = &config.workspace_lsp_roots;
@ -1124,26 +1124,27 @@ impl Editor {
.ok() .ok()
}); });
let doc = self.document_mut(doc_id)?;
let doc_url = doc.url()?;
if let Some(language_servers) = language_servers { if let Some(language_servers) = language_servers {
// only spawn new lang servers if the servers aren't the same let language_id = doc.language_id().map(ToOwned::to_owned).unwrap_or_default();
// TODO simplify?
let doc_language_servers = doc.language_servers().collect::<Vec<_>>(); // only spawn new language servers if the servers aren't the same
let spawn_new_servers = language_servers.len() != doc_language_servers.len()
|| language_servers let doc_language_servers_not_in_registry =
.iter() doc.language_servers.iter().filter(|(name, doc_ls)| {
.zip(doc_language_servers.iter()) !language_servers.contains_key(*name)
.any(|(l, dl)| l.id() != dl.id()); || language_servers[*name].id() != doc_ls.id()
if spawn_new_servers { });
for doc_language_server in doc_language_servers {
tokio::spawn(doc_language_server.text_document_did_close(doc.identifier())); for (_, language_server) in doc_language_servers_not_in_registry {
tokio::spawn(language_server.text_document_did_close(doc.identifier()));
} }
let language_id = doc.language_id().map(ToOwned::to_owned).unwrap_or_default(); let language_servers_not_in_doc = language_servers.iter().filter(|(name, ls)| {
!doc.language_servers.contains_key(*name)
|| doc.language_servers[*name].id() != ls.id()
});
for language_server in &language_servers { for (_, language_server) in language_servers_not_in_doc {
// TODO: this now races with on_init code if the init happens too quickly // TODO: this now races with on_init code if the init happens too quickly
tokio::spawn(language_server.text_document_did_open( tokio::spawn(language_server.text_document_did_open(
doc_url.clone(), doc_url.clone(),
@ -1153,8 +1154,7 @@ impl Editor {
)); ));
} }
doc.set_language_servers(language_servers); doc.language_servers = language_servers;
}
} }
Some(()) Some(())
} }

Loading…
Cancel
Save