on master help sign in
Refactored doc.language_servers and doc.language_servers_with_feature to return an iterator and refactor LanguageServerFeature handling to a HashMap (language server name maps to features)
76b5cabPhilipp Mildenberger
helix-core/src/syntax.rs+78−19
@@ -16,7 +16,7 @@ use slotmap::{DefaultKey as LayerId, HopSlotMap};
16use std::{
16use std::{
17 borrow::Cow,
17 borrow::Cow,
18 cell::RefCell,
18 cell::RefCell,
19− collections::{HashMap, VecDeque},
19+ collections::{HashMap, HashSet, VecDeque},
20 fmt::{self, Display},
20 fmt::{self, Display},
21 hash::{Hash, Hasher},
21 hash::{Hash, Hasher},
22 mem::{replace, transmute},
22 mem::{replace, transmute},
@@ -26,7 +26,7 @@ use std::{
26};
26};
27
27
28use once_cell::sync::{Lazy, OnceCell};
28use once_cell::sync::{Lazy, OnceCell};
29−use serde::{Deserialize, Serialize};
29+use serde::{ser::SerializeSeq, Deserialize, Serialize};
30
30
31use helix_loader::grammar::{get_language, load_runtime_file};
31use helix_loader::grammar::{get_language, load_runtime_file};
32
32
@@ -110,8 +110,13 @@ pub struct LanguageConfiguration {
110 #[serde(skip)]
110 #[serde(skip)]
111 pub(crate) highlight_config: OnceCell<Option<Arc<HighlightConfiguration>>>,
111 pub(crate) highlight_config: OnceCell<Option<Arc<HighlightConfiguration>>>,
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(
114+ default,
113− #[serde(default, skip_serializing_if = "Vec::is_empty")]
115+ skip_serializing_if = "HashMap::is_empty",
114− pub language_servers: Vec<LanguageServerFeatureConfiguration>,
116+ serialize_with = "serialize_lang_features",
117+ deserialize_with = "deserialize_lang_features"
118+ )]
119+ pub language_servers: HashMap<String, LanguageServerFeatures>,
115 #[serde(skip_serializing_if = "Option::is_none")]
120 #[serde(skip_serializing_if = "Option::is_none")]
116 pub indent: Option<IndentationConfiguration>,
121 pub indent: Option<IndentationConfiguration>,
117
122
@@ -211,7 +216,7 @@ impl<'de> Deserialize<'de> for FileType {
211 }
216 }
212}
217}
213
218
214−#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq)]
219+#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
215#[serde(rename_all = "kebab-case")]
220#[serde(rename_all = "kebab-case")]
216pub enum LanguageServerFeature {
221pub enum LanguageServerFeature {
217 Format,
222 Format,
@@ -261,18 +266,81 @@ impl Display for LanguageServerFeature {
261
266
262#[derive(Debug, Serialize, Deserialize)]
267#[derive(Debug, Serialize, Deserialize)]
263#[serde(untagged, rename_all = "kebab-case", deny_unknown_fields)]
268#[serde(untagged, rename_all = "kebab-case", deny_unknown_fields)]
264−pub enum LanguageServerFeatureConfiguration {
269+enum LanguageServerFeatureConfiguration {
265 #[serde(rename_all = "kebab-case")]
270 #[serde(rename_all = "kebab-case")]
266 Features {
271 Features {
267− #[serde(default, skip_serializing_if = "Vec::is_empty")]
272+ #[serde(default, skip_serializing_if = "HashSet::is_empty")]
268− only_features: Vec<LanguageServerFeature>,
273+ only_features: HashSet<LanguageServerFeature>,
269− #[serde(default, skip_serializing_if = "Vec::is_empty")]
274+ #[serde(default, skip_serializing_if = "HashSet::is_empty")]
270− except_features: Vec<LanguageServerFeature>,
275+ except_features: HashSet<LanguageServerFeature>,
271 name: String,
276 name: String,
272 },
277 },
273 Simple(String),
278 Simple(String),
274}
279}
275
280
281+#[derive(Debug, Default)]
282+pub struct LanguageServerFeatures {
283+ pub only: HashSet<LanguageServerFeature>,
284+ pub excluded: HashSet<LanguageServerFeature>,
285+}
286+
287+impl LanguageServerFeatures {
288+ pub fn has_feature(&self, feature: LanguageServerFeature) -> bool {
289+ self.only.is_empty() || self.only.contains(&feature) && !self.excluded.contains(&feature)
290+ }
291+}
292+
293+fn deserialize_lang_features<'de, D>(
294+ deserializer: D,
295+) -> Result<HashMap<String, LanguageServerFeatures>, D::Error>
296+where
297+ D: serde::Deserializer<'de>,
298+{
299+ let raw: Vec<LanguageServerFeatureConfiguration> = Deserialize::deserialize(deserializer)?;
300+ let res = raw
301+ .into_iter()
302+ .map(|config| match config {
303+ LanguageServerFeatureConfiguration::Simple(name) => {
304+ (name, LanguageServerFeatures::default())
305+ }
306+ LanguageServerFeatureConfiguration::Features {
307+ only_features,
308+ except_features,
309+ name,
310+ } => (
311+ name,
312+ LanguageServerFeatures {
313+ only: only_features,
314+ excluded: except_features,
315+ },
316+ ),
317+ })
318+ .collect();
319+ Ok(res)
320+}
321+fn serialize_lang_features<S>(
322+ map: &HashMap<String, LanguageServerFeatures>,
323+ serializer: S,
324+) -> Result<S::Ok, S::Error>
325+where
326+ S: serde::Serializer,
327+{
328+ let mut serializer = serializer.serialize_seq(Some(map.len()))?;
329+ for (name, features) in map {
330+ let features = if features.only.is_empty() && features.excluded.is_empty() {
331+ LanguageServerFeatureConfiguration::Simple(name.to_owned())
332+ } else {
333+ LanguageServerFeatureConfiguration::Features {
334+ only_features: features.only.clone(),
335+ except_features: features.excluded.clone(),
336+ name: name.to_owned(),
337+ }
338+ };
339+ serializer.serialize_element(&features)?;
340+ }
341+ serializer.end()
342+}
343+
276#[derive(Debug, Serialize, Deserialize)]
344#[derive(Debug, Serialize, Deserialize)]
277#[serde(rename_all = "kebab-case")]
345#[serde(rename_all = "kebab-case")]
278pub struct LanguageServerConfiguration {
346pub struct LanguageServerConfiguration {
@@ -650,15 +718,6 @@ pub struct SoftWrap {
650 pub wrap_at_text_width: Option<bool>,
718 pub wrap_at_text_width: Option<bool>,
651}
719}
652
720
653−impl LanguageServerFeatureConfiguration {
654− pub fn name(&self) -> &String {
655− match self {
656− LanguageServerFeatureConfiguration::Simple(name) => name,
657− LanguageServerFeatureConfiguration::Features { name, .. } => name,
658− }
659− }
660−}
661−
662// Expose loader as Lazy<> global since it's always static?
721// Expose loader as Lazy<> global since it's always static?
663
722
664#[derive(Debug)]
723#[derive(Debug)]
helix-lsp/src/lib.rs+10−10
@@ -689,12 +689,10 @@ 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− .iter()
692+ .keys()
693− .filter_map(|config| {
693+ .filter_map(|name| {
694− let name = config.name().clone();
695−
696 #[allow(clippy::map_entry)]
694 #[allow(clippy::map_entry)]
697− if self.inner.contains_key(&name) {
695+ if self.inner.contains_key(name) {
698 let client = match self.start_client(
696 let client = match self.start_client(
699 name.clone(),
697 name.clone(),
700 language_config,
698 language_config,
@@ -705,7 +703,10 @@ impl Registry {
705 Ok(client) => client,
703 Ok(client) => client,
706 error => return Some(error),
704 error => return Some(error),
707 };
705 };
706+ let old_clients = self
707+ .inner
708− let old_clients = self.inner.insert(name, vec![client.clone()]).unwrap();
708+ .insert(name.clone(), vec![client.clone()])
709+ .unwrap();
709
710
710 // TODO what if there are different language servers for different workspaces,
711 // TODO what if there are different language servers for different workspaces,
711 // I think the language servers will be stopped without being restarted, which is not intended
712 // I think the language servers will be stopped without being restarted, which is not intended
@@ -742,9 +743,8 @@ impl Registry {
742 ) -> Result<Vec<Arc<Client>>> {
743 ) -> Result<Vec<Arc<Client>>> {
743 language_config
744 language_config
744 .language_servers
745 .language_servers
745− .iter()
746+ .keys()
746− .map(|features| {
747+ .map(|name| {
747− let name = features.name();
748 if let Some(clients) = self.inner.get_mut(name) {
748 if let Some(clients) = self.inner.get_mut(name) {
749 if let Some((_, client)) = clients.iter_mut().enumerate().find(|(i, client)| {
749 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)
750 client.try_add_doc(&language_config.roots, root_dirs, doc_path, *i == 0)
@@ -759,7 +759,7 @@ impl Registry {
759 root_dirs,
759 root_dirs,
760 enable_snippets,
760 enable_snippets,
761 )?;
761 )?;
762− let clients = self.inner.entry(features.name().clone()).or_default();
762+ let clients = self.inner.entry(name.clone()).or_default();
763 clients.push(client.clone());
763 clients.push(client.clone());
764 Ok(client)
764 Ok(client)
765 })
765 })
helix-term/src/application.rs+4−4
@@ -699,9 +699,10 @@ impl Application {
699 tokio::spawn(language_server.did_change_configuration(config.clone()));
699 tokio::spawn(language_server.did_change_configuration(config.clone()));
700 }
700 }
701
701
702− let docs = self.editor.documents().filter(|doc| {
702+ let docs = self
703+ .editor
704+ .documents()
703− doc.language_servers().iter().any(|l| l.id() == server_id)
705+ .filter(|doc| doc.language_servers().any(|l| l.id() == server_id));
704− });
705
706
706 // trigger textDocument/didOpen for docs that are already open
707 // trigger textDocument/didOpen for docs that are already open
707 for doc in docs {
708 for doc in docs {
@@ -970,7 +971,6 @@ impl Application {
970 .filter_map(|doc| {
971 .filter_map(|doc| {
971 if doc
972 if doc
972 .language_servers()
973 .language_servers()
973− .iter()
974 .any(|server| server.id() == server_id)
974 .any(|server| server.id() == server_id)
975 {
975 {
976 doc.clear_diagnostics(server_id);
976 doc.clear_diagnostics(server_id);
helix-term/src/commands.rs+4−7
@@ -3235,7 +3235,6 @@ pub mod insert {
3235 let doc = doc_mut!(cx.editor);
3235 let doc = doc_mut!(cx.editor);
3236 let trigger_completion = doc
3236 let trigger_completion = doc
3237 .language_servers_with_feature(LanguageServerFeature::Completion)
3237 .language_servers_with_feature(LanguageServerFeature::Completion)
3238− .iter()
3239 .any(|ls| {
3238 .any(|ls| {
3240 let capabilities = ls.capabilities();
3239 let capabilities = ls.capabilities();
3241
3240
@@ -3264,7 +3263,6 @@ pub mod insert {
3264 // TODO support multiple language servers (not just the first that is found)
3263 // TODO support multiple language servers (not just the first that is found)
3265 let future = doc
3264 let future = doc
3266 .language_servers_with_feature(LanguageServerFeature::SignatureHelp)
3265 .language_servers_with_feature(LanguageServerFeature::SignatureHelp)
3267− .iter()
3268 .find_map(|ls| {
3266 .find_map(|ls| {
3269 let capabilities = ls.capabilities();
3267 let capabilities = ls.capabilities();
3270
3268
@@ -4067,10 +4065,8 @@ fn format_selections(cx: &mut Context) {
4067 .set_error("format_selections only supports a single selection for now");
4065 .set_error("format_selections only supports a single selection for now");
4068 return;
4066 return;
4069 }
4067 }
4070−
4068+ let future_offset_encoding = doc
4071− let (future, offset_encoding) = match doc
4072 .language_servers_with_feature(LanguageServerFeature::Format)
4069 .language_servers_with_feature(LanguageServerFeature::Format)
4073− .iter()
4074 .find_map(|language_server| {
4070 .find_map(|language_server| {
4075 let offset_encoding = language_server.offset_encoding();
4071 let offset_encoding = language_server.offset_encoding();
4076 let ranges: Vec<lsp::Range> = doc
4072 let ranges: Vec<lsp::Range> = doc
@@ -4091,7 +4087,9 @@ fn format_selections(cx: &mut Context) {
4091 None,
4087 None,
4092 )?;
4088 )?;
4093 Some((future, offset_encoding))
4089 Some((future, offset_encoding))
4094− }) {
4090+ });
4091+
4092+ let (future, offset_encoding) = match future_offset_encoding {
4095 Some(future_offset_encoding) => future_offset_encoding,
4093 Some(future_offset_encoding) => future_offset_encoding,
4096 None => {
4094 None => {
4097 cx.editor
4095 cx.editor
@@ -4247,7 +4245,6 @@ pub fn completion(cx: &mut Context) {
4247
4245
4248 let mut futures: FuturesUnordered<_> = doc
4246 let mut futures: FuturesUnordered<_> = doc
4249 .language_servers_with_feature(LanguageServerFeature::Completion)
4247 .language_servers_with_feature(LanguageServerFeature::Completion)
4250− .iter()
4251 // TODO this should probably already been filtered in something like "language_servers_with_feature"
4248 // TODO this should probably already been filtered in something like "language_servers_with_feature"
4252 .filter_map(|language_server| {
4249 .filter_map(|language_server| {
4253 let language_server_id = language_server.id();
4250 let language_server_id = language_server.id();
helix-term/src/commands/lsp.rs+25−30
@@ -353,7 +353,6 @@ pub fn symbol_picker(cx: &mut Context) {
353
353
354 let mut futures: FuturesUnordered<_> = doc
354 let mut futures: FuturesUnordered<_> = doc
355 .language_servers_with_feature(LanguageServerFeature::DocumentSymbols)
355 .language_servers_with_feature(LanguageServerFeature::DocumentSymbols)
356− .iter()
357 .filter_map(|ls| {
356 .filter_map(|ls| {
358 let request = ls.document_symbols(doc.identifier())?;
357 let request = ls.document_symbols(doc.identifier())?;
359 Some((request, ls.offset_encoding(), doc.identifier()))
358 Some((request, ls.offset_encoding(), doc.identifier()))
@@ -420,7 +419,6 @@ pub fn workspace_symbol_picker(cx: &mut Context) {
420 let doc = doc!(editor);
419 let doc = doc!(editor);
421 let mut futures: FuturesUnordered<_> = doc
420 let mut futures: FuturesUnordered<_> = doc
422 .language_servers_with_feature(LanguageServerFeature::WorkspaceSymbols)
421 .language_servers_with_feature(LanguageServerFeature::WorkspaceSymbols)
423− .iter()
424 .filter_map(|ls| Some((ls.workspace_symbols(pattern.clone())?, ls.offset_encoding())))
422 .filter_map(|ls| Some((ls.workspace_symbols(pattern.clone())?, ls.offset_encoding())))
425 .map(|(request, offset_encoding)| async move {
423 .map(|(request, offset_encoding)| async move {
426 let json = request.await?;
424 let json = request.await?;
@@ -581,7 +579,6 @@ pub fn code_action(cx: &mut Context) {
581
579
582 let mut futures: FuturesUnordered<_> = doc
580 let mut futures: FuturesUnordered<_> = doc
583 .language_servers_with_feature(LanguageServerFeature::CodeAction)
581 .language_servers_with_feature(LanguageServerFeature::CodeAction)
584− .iter()
585 // TODO this should probably already been filtered in something like "language_servers_with_feature"
582 // TODO this should probably already been filtered in something like "language_servers_with_feature"
586 .filter_map(|language_server| {
583 .filter_map(|language_server| {
587 let offset_encoding = language_server.offset_encoding();
584 let offset_encoding = language_server.offset_encoding();
@@ -1034,15 +1031,15 @@ fn to_locations(definitions: Option<lsp::GotoDefinitionResponse>) -> Vec<lsp::Lo
1034// TODO find a way to reduce boilerplate of all the goto functions, without unnecessary complexity...
1031// TODO find a way to reduce boilerplate of all the goto functions, without unnecessary complexity...
1035pub fn goto_declaration(cx: &mut Context) {
1032pub fn goto_declaration(cx: &mut Context) {
1036 let (view, doc) = current!(cx.editor);
1033 let (view, doc) = current!(cx.editor);
1037− let (future, offset_encoding) = match doc
1034+ let future_offset_encoding = doc
1038 .language_servers_with_feature(LanguageServerFeature::GotoDeclaration)
1035 .language_servers_with_feature(LanguageServerFeature::GotoDeclaration)
1039− .iter()
1040 .find_map(|language_server| {
1036 .find_map(|language_server| {
1041 let offset_encoding = language_server.offset_encoding();
1037 let offset_encoding = language_server.offset_encoding();
1042 let pos = doc.position(view.id, offset_encoding);
1038 let pos = doc.position(view.id, offset_encoding);
1043 let future = language_server.goto_declaration(doc.identifier(), pos, None)?;
1039 let future = language_server.goto_declaration(doc.identifier(), pos, None)?;
1044 Some((future, offset_encoding))
1040 Some((future, offset_encoding))
1045− }) {
1041+ });
1042+ let (future, offset_encoding) = match future_offset_encoding {
1046 Some(future_offset_encoding) => future_offset_encoding,
1043 Some(future_offset_encoding) => future_offset_encoding,
1047 None => {
1044 None => {
1048 cx.editor
1045 cx.editor
@@ -1062,15 +1059,15 @@ pub fn goto_declaration(cx: &mut Context) {
1062
1059
1063pub fn goto_definition(cx: &mut Context) {
1060pub fn goto_definition(cx: &mut Context) {
1064 let (view, doc) = current!(cx.editor);
1061 let (view, doc) = current!(cx.editor);
1065− let (future, offset_encoding) = match doc
1062+ let future_offset_encoding = doc
1066 .language_servers_with_feature(LanguageServerFeature::GotoDefinition)
1063 .language_servers_with_feature(LanguageServerFeature::GotoDefinition)
1067− .iter()
1068 .find_map(|language_server| {
1064 .find_map(|language_server| {
1069 let offset_encoding = language_server.offset_encoding();
1065 let offset_encoding = language_server.offset_encoding();
1070 let pos = doc.position(view.id, offset_encoding);
1066 let pos = doc.position(view.id, offset_encoding);
1071 let future = language_server.goto_definition(doc.identifier(), pos, None)?;
1067 let future = language_server.goto_definition(doc.identifier(), pos, None)?;
1072 Some((future, offset_encoding))
1068 Some((future, offset_encoding))
1073− }) {
1069+ });
1070+ let (future, offset_encoding) = match future_offset_encoding {
1074 Some(future_offset_encoding) => future_offset_encoding,
1071 Some(future_offset_encoding) => future_offset_encoding,
1075 None => {
1072 None => {
1076 cx.editor
1073 cx.editor
@@ -1090,15 +1087,15 @@ pub fn goto_definition(cx: &mut Context) {
1090
1087
1091pub fn goto_type_definition(cx: &mut Context) {
1088pub fn goto_type_definition(cx: &mut Context) {
1092 let (view, doc) = current!(cx.editor);
1089 let (view, doc) = current!(cx.editor);
1093− let (future, offset_encoding) = match doc
1090+ let future_offset_encoding = doc
1094 .language_servers_with_feature(LanguageServerFeature::GotoTypeDefinition)
1091 .language_servers_with_feature(LanguageServerFeature::GotoTypeDefinition)
1095− .iter()
1096 .find_map(|language_server| {
1092 .find_map(|language_server| {
1097 let offset_encoding = language_server.offset_encoding();
1093 let offset_encoding = language_server.offset_encoding();
1098 let pos = doc.position(view.id, offset_encoding);
1094 let pos = doc.position(view.id, offset_encoding);
1099 let future = language_server.goto_type_definition(doc.identifier(), pos, None)?;
1095 let future = language_server.goto_type_definition(doc.identifier(), pos, None)?;
1100 Some((future, offset_encoding))
1096 Some((future, offset_encoding))
1101− }) {
1097+ });
1098+ let (future, offset_encoding) = match future_offset_encoding {
1102 Some(future_offset_encoding) => future_offset_encoding,
1099 Some(future_offset_encoding) => future_offset_encoding,
1103 None => {
1100 None => {
1104 cx.editor
1101 cx.editor
@@ -1118,15 +1115,15 @@ pub fn goto_type_definition(cx: &mut Context) {
1118
1115
1119pub fn goto_implementation(cx: &mut Context) {
1116pub fn goto_implementation(cx: &mut Context) {
1120 let (view, doc) = current!(cx.editor);
1117 let (view, doc) = current!(cx.editor);
1121− let (future, offset_encoding) = match doc
1118+ let future_offset_encoding = doc
1122 .language_servers_with_feature(LanguageServerFeature::GotoImplementation)
1119 .language_servers_with_feature(LanguageServerFeature::GotoImplementation)
1123− .iter()
1124 .find_map(|language_server| {
1120 .find_map(|language_server| {
1125 let offset_encoding = language_server.offset_encoding();
1121 let offset_encoding = language_server.offset_encoding();
1126 let pos = doc.position(view.id, offset_encoding);
1122 let pos = doc.position(view.id, offset_encoding);
1127 let future = language_server.goto_implementation(doc.identifier(), pos, None)?;
1123 let future = language_server.goto_implementation(doc.identifier(), pos, None)?;
1128 Some((future, offset_encoding))
1124 Some((future, offset_encoding))
1129− }) {
1125+ });
1126+ let (future, offset_encoding) = match future_offset_encoding {
1130 Some(future_offset_encoding) => future_offset_encoding,
1127 Some(future_offset_encoding) => future_offset_encoding,
1131 None => {
1128 None => {
1132 cx.editor
1129 cx.editor
@@ -1147,9 +1144,8 @@ pub fn goto_implementation(cx: &mut Context) {
1147pub fn goto_reference(cx: &mut Context) {
1144pub fn goto_reference(cx: &mut Context) {
1148 let config = cx.editor.config();
1145 let config = cx.editor.config();
1149 let (view, doc) = current!(cx.editor);
1146 let (view, doc) = current!(cx.editor);
1150− let (future, offset_encoding) = match doc
1147+ let future_offset_encoding = doc
1151 .language_servers_with_feature(LanguageServerFeature::GotoReference)
1148 .language_servers_with_feature(LanguageServerFeature::GotoReference)
1152− .iter()
1153 .find_map(|language_server| {
1149 .find_map(|language_server| {
1154 let offset_encoding = language_server.offset_encoding();
1150 let offset_encoding = language_server.offset_encoding();
1155 let pos = doc.position(view.id, offset_encoding);
1151 let pos = doc.position(view.id, offset_encoding);
@@ -1160,7 +1156,8 @@ pub fn goto_reference(cx: &mut Context) {
1160 None,
1156 None,
1161 )?;
1157 )?;
1162 Some((future, offset_encoding))
1158 Some((future, offset_encoding))
1163− }) {
1159+ });
1160+ let (future, offset_encoding) = match future_offset_encoding {
1164 Some(future_offset_encoding) => future_offset_encoding,
1161 Some(future_offset_encoding) => future_offset_encoding,
1165 None => {
1162 None => {
1166 cx.editor
1163 cx.editor
@@ -1192,13 +1189,14 @@ pub fn signature_help_impl(cx: &mut Context, invoked: SignatureHelpInvoked) {
1192 let (view, doc) = current!(cx.editor);
1189 let (view, doc) = current!(cx.editor);
1193
1190
1194 // TODO merge multiple language server signature help into one instead of just taking the first language server that supports it
1191 // TODO merge multiple language server signature help into one instead of just taking the first language server that supports it
1195− let future = match doc
1192+ let future = doc
1196 .language_servers_with_feature(LanguageServerFeature::SignatureHelp)
1193 .language_servers_with_feature(LanguageServerFeature::SignatureHelp)
1197− .iter()
1198 .find_map(|language_server| {
1194 .find_map(|language_server| {
1199 let pos = doc.position(view.id, language_server.offset_encoding());
1195 let pos = doc.position(view.id, language_server.offset_encoding());
1200 language_server.text_document_signature_help(doc.identifier(), pos, None)
1196 language_server.text_document_signature_help(doc.identifier(), pos, None)
1201− }) {
1197+ });
1198+
1199+ let future = match future {
1202 Some(future) => future.boxed(),
1200 Some(future) => future.boxed(),
1203 None => {
1201 None => {
1204 // Do not show the message if signature help was invoked
1202 // Do not show the message if signature help was invoked
@@ -1328,7 +1326,6 @@ pub fn hover(cx: &mut Context) {
1328 // TODO: factor out a doc.position_identifier() that returns lsp::TextDocumentPositionIdentifier
1326 // TODO: factor out a doc.position_identifier() that returns lsp::TextDocumentPositionIdentifier
1329 let request = doc
1327 let request = doc
1330 .language_servers_with_feature(LanguageServerFeature::Hover)
1328 .language_servers_with_feature(LanguageServerFeature::Hover)
1331− .iter()
1332 .find_map(|language_server| {
1329 .find_map(|language_server| {
1333 let pos = doc.position(view.id, language_server.offset_encoding());
1330 let pos = doc.position(view.id, language_server.offset_encoding());
1334 language_server.text_document_hover(doc.identifier(), pos, None)
1331 language_server.text_document_hover(doc.identifier(), pos, None)
@@ -1436,7 +1433,6 @@ pub fn rename_symbol(cx: &mut Context) {
1436 let (view, doc) = current!(cx.editor);
1433 let (view, doc) = current!(cx.editor);
1437 let request = doc
1434 let request = doc
1438 .language_servers_with_feature(LanguageServerFeature::RenameSymbol)
1435 .language_servers_with_feature(LanguageServerFeature::RenameSymbol)
1439− .iter()
1440 .find_map(|language_server| {
1436 .find_map(|language_server| {
1441 if let Some(language_server_id) = language_server_id {
1437 if let Some(language_server_id) = language_server_id {
1442 if language_server.id() != language_server_id {
1438 if language_server.id() != language_server_id {
@@ -1475,7 +1471,6 @@ pub fn rename_symbol(cx: &mut Context) {
1475
1471
1476 let prepare_rename_request = doc
1472 let prepare_rename_request = doc
1477 .language_servers_with_feature(LanguageServerFeature::RenameSymbol)
1473 .language_servers_with_feature(LanguageServerFeature::RenameSymbol)
1478− .iter()
1479 .find_map(|language_server| {
1474 .find_map(|language_server| {
1480 let offset_encoding = language_server.offset_encoding();
1475 let offset_encoding = language_server.offset_encoding();
1481 let pos = doc.position(view.id, offset_encoding);
1476 let pos = doc.position(view.id, offset_encoding);
@@ -1516,17 +1511,17 @@ pub fn rename_symbol(cx: &mut Context) {
1516
1511
1517pub fn select_references_to_symbol_under_cursor(cx: &mut Context) {
1512pub fn select_references_to_symbol_under_cursor(cx: &mut Context) {
1518 let (view, doc) = current!(cx.editor);
1513 let (view, doc) = current!(cx.editor);
1519− let (future, offset_encoding) = match doc
1514+ let future_offset_encoding = doc
1520 .language_servers_with_feature(LanguageServerFeature::DocumentHighlight)
1515 .language_servers_with_feature(LanguageServerFeature::DocumentHighlight)
1521− .iter()
1522 .find_map(|language_server| {
1516 .find_map(|language_server| {
1523 let offset_encoding = language_server.offset_encoding();
1517 let offset_encoding = language_server.offset_encoding();
1524 let pos = doc.position(view.id, offset_encoding);
1518 let pos = doc.position(view.id, offset_encoding);
1525 let future =
1519 let future =
1526 language_server.text_document_document_highlight(doc.identifier(), pos, None)?;
1520 language_server.text_document_document_highlight(doc.identifier(), pos, None)?;
1527 Some((future, offset_encoding))
1521 Some((future, offset_encoding))
1528− }) {
1522+ });
1529− Some(future) => future,
1523+ let (future, offset_encoding) = match future_offset_encoding {
1524+ Some(future_offset_encoding) => future_offset_encoding,
1530 None => {
1525 None => {
1531 cx.editor
1526 cx.editor
1532 .set_error("No language server supports document-highlight");
1527 .set_error("No language server supports document-highlight");
@@ -1587,8 +1582,8 @@ fn compute_inlay_hints_for_view(
1587 let view_id = view.id;
1582 let view_id = view.id;
1588 let doc_id = view.doc;
1583 let doc_id = view.doc;
1589
1584
1590− let language_servers = doc.language_servers_with_feature(LanguageServerFeature::InlayHints);
1585+ let mut language_servers = doc.language_servers_with_feature(LanguageServerFeature::InlayHints);
1591− let language_server = language_servers.iter().find(|language_server| {
1586+ let language_server = language_servers.find(|language_server| {
1592 matches!(
1587 matches!(
1593 language_server.capabilities().inlay_hint_provider,
1588 language_server.capabilities().inlay_hint_provider,
1594 Some(
1589 Some(
helix-term/src/commands/typed.rs+12−12
@@ -1330,14 +1330,16 @@ fn lsp_workspace_command(
1330 return Ok(());
1330 return Ok(());
1331 }
1331 }
1332 let doc = doc!(cx.editor);
1332 let doc = doc!(cx.editor);
1333− let language_servers =
1333+ let id_options = doc
1334− doc.language_servers_with_feature(LanguageServerFeature::WorkspaceCommand);
1334+ .language_servers_with_feature(LanguageServerFeature::WorkspaceCommand)
1335− let (language_server_id, options) = match language_servers.iter().find_map(|ls| {
1335+ .find_map(|ls| {
1336− ls.capabilities()
1336+ ls.capabilities()
1337− .execute_command_provider
1337+ .execute_command_provider
1338− .as_ref()
1338+ .as_ref()
1339− .map(|options| (ls.id(), options))
1339+ .map(|options| (ls.id(), options))
1340− }) {
1340+ });
1341+
1342+ let (language_server_id, options) = match id_options {
1341 Some(id_options) => id_options,
1343 Some(id_options) => id_options,
1342 None => {
1344 None => {
1343 cx.editor.set_status(
1345 cx.editor.set_status(
@@ -1346,6 +1348,7 @@ fn lsp_workspace_command(
1346 return Ok(());
1348 return Ok(());
1347 }
1349 }
1348 };
1350 };
1351+
1349 if args.is_empty() {
1352 if args.is_empty() {
1350 let commands = options
1353 let commands = options
1351 .commands
1354 .commands
@@ -1445,7 +1448,6 @@ fn lsp_stop(
1445 // I'm not sure if this is really what we want
1448 // I'm not sure if this is really what we want
1446 let ls_shutdown_names = doc
1449 let ls_shutdown_names = doc
1447 .language_servers()
1450 .language_servers()
1448− .iter()
1449 .map(|ls| ls.name())
1451 .map(|ls| ls.name())
1450 .collect::<Vec<_>>();
1452 .collect::<Vec<_>>();
1451
1453
@@ -1459,7 +1461,6 @@ fn lsp_stop(
1459 .filter_map(|doc| {
1461 .filter_map(|doc| {
1460 let doc_active_ls_ids: Vec<_> = doc
1462 let doc_active_ls_ids: Vec<_> = doc
1461 .language_servers()
1463 .language_servers()
1462− .iter()
1463 .filter(|ls| !ls_shutdown_names.contains(&ls.name()))
1464 .filter(|ls| !ls_shutdown_names.contains(&ls.name()))
1464 .map(|ls| ls.id())
1465 .map(|ls| ls.id())
1465 .collect();
1466 .collect();
@@ -1472,7 +1473,7 @@ fn lsp_stop(
1472 .map(Clone::clone)
1473 .map(Clone::clone)
1473 .collect();
1474 .collect();
1474
1475
1475− if active_clients.len() != doc.language_servers().len() {
1476+ if active_clients.len() != doc.language_servers().count() {
1476 Some((doc.id(), active_clients))
1477 Some((doc.id(), active_clients))
1477 } else {
1478 } else {
1478 None
1479 None
@@ -1485,7 +1486,6 @@ fn lsp_stop(
1485
1486
1486 let stopped_clients: Vec<_> = doc
1487 let stopped_clients: Vec<_> = doc
1487 .language_servers()
1488 .language_servers()
1488− .iter()
1489 .filter(|ls| {
1489 .filter(|ls| {
1490 !active_clients
1490 !active_clients
1491 .iter()
1491 .iter()
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.first().and_then(|lsp| {
197+ let lsp = lang.language_servers.keys().next().and_then(|ls_name| {
198 syn_loader_conf
198 syn_loader_conf
199 .language_server
199 .language_server
200− .get(lsp.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.first().and_then(|lsp| {
274+ lang.language_servers.keys().next().and_then(|ls_name| {
275 syn_loader_conf
275 syn_loader_conf
276 .language_server
276 .language_server
277− .get(lsp.name())
277+ .get(ls_name)
278 .map(|config| config.command.clone())
278 .map(|config| config.command.clone())
279 }),
279 }),
280 )?;
280 )?;
helix-term/src/ui/mod.rs+3−5
@@ -394,13 +394,11 @@ pub mod completers {
394 pub fn lsp_workspace_command(editor: &Editor, input: &str) -> Vec<Completion> {
394 pub fn lsp_workspace_command(editor: &Editor, input: &str) -> Vec<Completion> {
395 let matcher = Matcher::default();
395 let matcher = Matcher::default();
396
396
397− let language_servers =
397+ let options = match doc!(editor)
398− doc!(editor).language_servers_with_feature(LanguageServerFeature::WorkspaceCommand);
398+ .language_servers_with_feature(LanguageServerFeature::WorkspaceCommand)
399− let options = match language_servers
400− .into_iter()
401 .find_map(|ls| ls.capabilities().execute_command_provider.as_ref())
399 .find_map(|ls| ls.capabilities().execute_command_provider.as_ref())
402 {
400 {
403− Some(id_options) => id_options,
401+ Some(options) => options,
404 None => {
402 None => {
405 return vec![];
403 return vec![];
406 }
404 }
helix-term/src/ui/statusline.rs+2−3
@@ -202,11 +202,10 @@ fn render_lsp_spinner<F>(context: &mut RenderContext, write: F)
202where
202where
203 F: Fn(&mut RenderContext, String, Option<Style>) + Copy,
203 F: Fn(&mut RenderContext, String, Option<Style>) + Copy,
204{
204{
205− let language_servers = context.doc.language_servers();
205+ let language_server = context.doc.language_servers().next();
206 write(
206 write(
207 context,
207 context,
208− language_servers
208+ language_server
209− .first()
210 .and_then(|srv| {
209 .and_then(|srv| {
211 context
210 context
212 .spinners
211 .spinners
helix-view/src/document.rs+10−42
@@ -6,7 +6,7 @@ use futures_util::FutureExt;
6use helix_core::auto_pairs::AutoPairs;
6use helix_core::auto_pairs::AutoPairs;
7use helix_core::doc_formatter::TextFormat;
7use helix_core::doc_formatter::TextFormat;
8use helix_core::encoding::Encoding;
8use helix_core::encoding::Encoding;
9−use helix_core::syntax::{Highlight, LanguageServerFeature, LanguageServerFeatureConfiguration};
9+use helix_core::syntax::{Highlight, LanguageServerFeature};
10use helix_core::text_annotations::{InlineAnnotation, TextAnnotations};
10use helix_core::text_annotations::{InlineAnnotation, TextAnnotations};
11use helix_core::Range;
11use helix_core::Range;
12use helix_vcs::{DiffHandle, DiffProviderRegistry};
12use helix_vcs::{DiffHandle, DiffProviderRegistry};
@@ -734,7 +734,6 @@ impl Document {
734 // finds first language server that supports formatting and then formats
734 // finds first language server that supports formatting and then formats
735 let (offset_encoding, request) = self
735 let (offset_encoding, request) = self
736 .language_servers_with_feature(LanguageServerFeature::Format)
736 .language_servers_with_feature(LanguageServerFeature::Format)
737− .iter()
738 .find_map(|language_server| {
737 .find_map(|language_server| {
739 let offset_encoding = language_server.offset_encoding();
738 let offset_encoding = language_server.offset_encoding();
740 let request = language_server.text_document_formatting(
739 let request = language_server.text_document_formatting(
@@ -1437,54 +1436,24 @@ impl Document {
1437 self.version
1436 self.version
1438 }
1437 }
1439
1438
1440− /// Language servers that have been initialized.
1441− pub fn language_servers(&self) -> Vec<&helix_lsp::Client> {
1439+ pub fn language_servers(&self) -> impl Iterator<Item = &helix_lsp::Client> {
1442 self.language_servers
1440 self.language_servers
1443 .iter()
1441 .iter()
1444 .filter_map(|l| if l.is_initialized() { Some(&**l) } else { None })
1442 .filter_map(|l| if l.is_initialized() { Some(&**l) } else { None })
1445− .collect()
1446 }
1443 }
1447
1444
1448 // TODO filter also based on LSP capabilities?
1445 // TODO filter also based on LSP capabilities?
1449 pub fn language_servers_with_feature(
1446 pub fn language_servers_with_feature(
1450 &self,
1447 &self,
1451 feature: LanguageServerFeature,
1448 feature: LanguageServerFeature,
1452− ) -> Vec<&helix_lsp::Client> {
1449+ ) -> impl Iterator<Item = &helix_lsp::Client> {
1453− let language_servers = self.language_servers();
1450+ self.language_servers().filter(move |server| {
1454−
1455− let language_config = match self.language_config() {
1456− Some(language_config) => language_config,
1451+ self.language_config()
1457− None => return Vec::new(),
1452+ .and_then(|config| config.language_servers.get(server.name()))
1458− };
1453+ .map_or(false, |server_features| {
1459−
1454+ server_features.has_feature(feature)
1460− // O(n^2) but since language_servers will be of very small length,
1461− // I don't see the necessity to optimize
1462− language_config
1463− .language_servers
1464− .iter()
1465− .filter_map(|c| match c {
1466− LanguageServerFeatureConfiguration::Simple(name) => language_servers
1467− .iter()
1468− .find(|ls| ls.name() == name)
1469− .copied(),
1470− LanguageServerFeatureConfiguration::Features {
1471− only_features,
1472− except_features,
1473− name,
1474− } => {
1475− if (only_features.is_empty() || only_features.contains(&feature))
1476− && !except_features.contains(&feature)
1477− {
1478− language_servers
1479− .iter()
1480− .find(|ls| ls.name() == name)
1481− .copied()
1482− } else {
1483− None
1484− }
1485− }
1455+ })
1486− })
1456+ })
1487− .collect()
1488 }
1457 }
1489
1458
1490 pub fn diff_handle(&self) -> Option<&DiffHandle> {
1459 pub fn diff_handle(&self) -> Option<&DiffHandle> {
@@ -1610,7 +1579,6 @@ impl Document {
1610 pub fn shown_diagnostics(&self) -> impl Iterator<Item = &Diagnostic> + DoubleEndedIterator {
1579 pub fn shown_diagnostics(&self) -> impl Iterator<Item = &Diagnostic> + DoubleEndedIterator {
1611 let ls_ids: HashSet<_> = self
1580 let ls_ids: HashSet<_> = self
1612 .language_servers_with_feature(LanguageServerFeature::Diagnostics)
1581 .language_servers_with_feature(LanguageServerFeature::Diagnostics)
1613− .iter()
1614 .map(|ls| ls.id())
1582 .map(|ls| ls.id())
1615 .collect();
1583 .collect();
1616 self.diagnostics
1584 self.diagnostics
helix-view/src/editor.rs+3−2
@@ -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
@@ -1129,7 +1129,8 @@ impl Editor {
1129
1129
1130 if let Some(language_servers) = language_servers {
1130 if let Some(language_servers) = language_servers {
1131 // only spawn new lang servers if the servers aren't the same
1131 // only spawn new lang servers if the servers aren't the same
1132+ // TODO simplify?
1132− let doc_language_servers = doc.language_servers();
1133+ let doc_language_servers = doc.language_servers().collect::<Vec<_>>();
1133 let spawn_new_servers = language_servers.len() != doc_language_servers.len()
1134 let spawn_new_servers = language_servers.len() != doc_language_servers.len()
1134 || language_servers
1135 || language_servers
1135 .iter()
1136 .iter()