on master help sign in
Remove boilerplate in the goto methods by generically composing functions
60a6af1Philipp Mildenberger
helix-term/src/commands/lsp.rs+64−125
@@ -6,7 +6,7 @@ use helix_lsp::{
6 NumberOrString,
6 NumberOrString,
7 },
7 },
8 util::{diagnostic_to_lsp_diagnostic, lsp_range_to_range, range_to_lsp_range},
8 util::{diagnostic_to_lsp_diagnostic, lsp_range_to_range, range_to_lsp_range},
9− OffsetEncoding,
9+ Client, OffsetEncoding,
10};
10};
11use serde_json::Value;
11use serde_json::Value;
12use tokio_stream::StreamExt;
12use tokio_stream::StreamExt;
@@ -1028,151 +1028,90 @@ fn to_locations(definitions: Option<lsp::GotoDefinitionResponse>) -> Vec<lsp::Lo
1028 }
1028 }
1029}
1029}
1030
1030
1031−// TODO find a way to reduce boilerplate of all the goto functions, without unnecessary complexity...
1031+fn goto_single_impl<P, F>(cx: &mut Context, feature: LanguageServerFeature, request_provider: P)
1032−pub fn goto_declaration(cx: &mut Context) {
1032+where
1033+ P: Fn(&Client, lsp::Position, lsp::TextDocumentIdentifier) -> Option<F>,
1034+ F: Future<Output = helix_lsp::Result<serde_json::Value>> + 'static + Send,
1035+{
1033 let (view, doc) = current!(cx.editor);
1036 let (view, doc) = current!(cx.editor);
1034− let future_offset_encoding = doc
1037+ if let Some((future, offset_encoding)) =
1035− .language_servers_with_feature(LanguageServerFeature::GotoDeclaration)
1038+ doc.run_on_first_supported_language_server(view.id, feature, |ls, encoding, pos, doc_id| {
1036− .find_map(|language_server| {
1039+ Some((request_provider(ls, pos, doc_id)?, encoding))
1037− let offset_encoding = language_server.offset_encoding();
1038− let pos = doc.position(view.id, offset_encoding);
1039− let future = language_server.goto_declaration(doc.identifier(), pos, None)?;
1040− Some((future, offset_encoding))
1041− });
1040+ })
1042− let (future, offset_encoding) = match future_offset_encoding {
1041+ {
1043− Some(future_offset_encoding) => future_offset_encoding,
1042+ cx.callback(
1044− None => {
1043+ future,
1045− cx.editor
1044+ move |editor, compositor, response: Option<lsp::GotoDefinitionResponse>| {
1045+ let items = to_locations(response);
1046+ goto_impl(editor, compositor, items, offset_encoding);
1047+ },
1048+ );
1049+ } else {
1046− .set_error("No language server supports goto-declaration");
1050+ cx.editor.set_error("No language server supports {feature}");
1047− return;
1048− }
1051+ }
1049− };
1052+}
1050
1053
1051− cx.callback(
1054+pub fn goto_declaration(cx: &mut Context) {
1052− future,
1055+ goto_single_impl(
1053− move |editor, compositor, response: Option<lsp::GotoDefinitionResponse>| {
1056+ cx,
1054− let items = to_locations(response);
1057+ LanguageServerFeature::GotoDeclaration,
1055− goto_impl(editor, compositor, items, offset_encoding);
1058+ |ls, pos, doc_id| ls.goto_declaration(doc_id, pos, None),
1056− },
1057 );
1059 );
1058}
1060}
1059
1061
1060pub fn goto_definition(cx: &mut Context) {
1062pub fn goto_definition(cx: &mut Context) {
1061− let (view, doc) = current!(cx.editor);
1063+ goto_single_impl(
1062− let future_offset_encoding = doc
1064+ cx,
1063− .language_servers_with_feature(LanguageServerFeature::GotoDefinition)
1065+ LanguageServerFeature::GotoDefinition,
1064− .find_map(|language_server| {
1065− let offset_encoding = language_server.offset_encoding();
1066− let pos = doc.position(view.id, offset_encoding);
1067− let future = language_server.goto_definition(doc.identifier(), pos, None)?;
1066+ |ls, pos, doc_id| ls.goto_definition(doc_id, pos, None),
1068− Some((future, offset_encoding))
1069− });
1070− let (future, offset_encoding) = match future_offset_encoding {
1071− Some(future_offset_encoding) => future_offset_encoding,
1072− None => {
1073− cx.editor
1074− .set_error("No language server supports goto-definition");
1075− return;
1076− }
1077− };
1078−
1079− cx.callback(
1080− future,
1081− move |editor, compositor, response: Option<lsp::GotoDefinitionResponse>| {
1082− let items = to_locations(response);
1083− goto_impl(editor, compositor, items, offset_encoding);
1084− },
1085 );
1067 );
1086}
1068}
1087
1069
1088pub fn goto_type_definition(cx: &mut Context) {
1070pub fn goto_type_definition(cx: &mut Context) {
1089− let (view, doc) = current!(cx.editor);
1071+ goto_single_impl(
1090− let future_offset_encoding = doc
1072+ cx,
1091− .language_servers_with_feature(LanguageServerFeature::GotoTypeDefinition)
1073+ LanguageServerFeature::GotoTypeDefinition,
1092− .find_map(|language_server| {
1093− let offset_encoding = language_server.offset_encoding();
1094− let pos = doc.position(view.id, offset_encoding);
1095− let future = language_server.goto_type_definition(doc.identifier(), pos, None)?;
1074+ |ls, pos, doc_id| ls.goto_type_definition(doc_id, pos, None),
1096− Some((future, offset_encoding))
1097− });
1098− let (future, offset_encoding) = match future_offset_encoding {
1099− Some(future_offset_encoding) => future_offset_encoding,
1100− None => {
1101− cx.editor
1102− .set_error("No language server supports goto-type-definition");
1103− return;
1104− }
1105− };
1106−
1107− cx.callback(
1108− future,
1109− move |editor, compositor, response: Option<lsp::GotoDefinitionResponse>| {
1110− let items = to_locations(response);
1111− goto_impl(editor, compositor, items, offset_encoding);
1112− },
1113 );
1075 );
1114}
1076}
1115
1077
1116pub fn goto_implementation(cx: &mut Context) {
1078pub fn goto_implementation(cx: &mut Context) {
1117− let (view, doc) = current!(cx.editor);
1079+ goto_single_impl(
1118− let future_offset_encoding = doc
1080+ cx,
1119− .language_servers_with_feature(LanguageServerFeature::GotoImplementation)
1081+ LanguageServerFeature::GotoImplementation,
1120− .find_map(|language_server| {
1121− let offset_encoding = language_server.offset_encoding();
1122− let pos = doc.position(view.id, offset_encoding);
1123− let future = language_server.goto_implementation(doc.identifier(), pos, None)?;
1082+ |ls, pos, doc_id| ls.goto_implementation(doc_id, pos, None),
1124− Some((future, offset_encoding))
1125− });
1126− let (future, offset_encoding) = match future_offset_encoding {
1127− Some(future_offset_encoding) => future_offset_encoding,
1128− None => {
1129− cx.editor
1130− .set_error("No language server supports goto-implementation");
1131− return;
1132− }
1133− };
1134−
1135− cx.callback(
1136− future,
1137− move |editor, compositor, response: Option<lsp::GotoDefinitionResponse>| {
1138− let items = to_locations(response);
1139− goto_impl(editor, compositor, items, offset_encoding);
1140− },
1141 );
1083 );
1142}
1084}
1143
1085
1144pub fn goto_reference(cx: &mut Context) {
1086pub fn goto_reference(cx: &mut Context) {
1145 let config = cx.editor.config();
1087 let config = cx.editor.config();
1146 let (view, doc) = current!(cx.editor);
1088 let (view, doc) = current!(cx.editor);
1147− let future_offset_encoding = doc
1089+ if let Some((future, offset_encoding)) = doc.run_on_first_supported_language_server(
1090+ view.id,
1148− .language_servers_with_feature(LanguageServerFeature::GotoReference)
1091+ LanguageServerFeature::GotoReference,
1149− .find_map(|language_server| {
1092+ |ls, encoding, pos, doc_id| {
1150− let offset_encoding = language_server.offset_encoding();
1093+ Some((
1151− let pos = doc.position(view.id, offset_encoding);
1152− let future = language_server.goto_reference(
1094+ ls.goto_reference(
1153− doc.identifier(),
1095+ doc_id,
1154− pos,
1096+ pos,
1155− config.lsp.goto_reference_include_declaration,
1097+ config.lsp.goto_reference_include_declaration,
1156− None,
1098+ None,
1157− )?;
1099+ )?,
1158− Some((future, offset_encoding))
1100+ encoding,
1159− });
1101+ ))
1160− let (future, offset_encoding) = match future_offset_encoding {
1161− Some(future_offset_encoding) => future_offset_encoding,
1162− None => {
1163− cx.editor
1164− .set_error("No language server supports goto-reference");
1165− return;
1166− }
1167− };
1168−
1169− cx.callback(
1170− future,
1171− move |editor, compositor, response: Option<Vec<lsp::Location>>| {
1172− let items = response.unwrap_or_default();
1173− goto_impl(editor, compositor, items, offset_encoding);
1174 },
1102 },
1175− );
1103+ ) {
1104+ cx.callback(
1105+ future,
1106+ move |editor, compositor, response: Option<Vec<lsp::Location>>| {
1107+ let items = response.unwrap_or_default();
1108+ goto_impl(editor, compositor, items, offset_encoding);
1109+ },
1110+ );
1111+ } else {
1112+ cx.editor
1113+ .set_error("No language server supports goto-reference");
1114+ }
1176}
1115}
1177
1116
1178#[derive(PartialEq, Eq, Clone, Copy)]
1117#[derive(PartialEq, Eq, Clone, Copy)]
helix-view/src/document.rs+18−1
@@ -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;
583+use helix_lsp::{lsp, Client, OffsetEncoding};
584use url::Url;
584use url::Url;
585
585
586impl Document {
586impl Document {
@@ -1460,6 +1460,23 @@ impl Document {
1460 self.language_servers().any(|l| l.id() == id)
1460 self.language_servers().any(|l| l.id() == id)
1461 }
1461 }
1462
1462
1463+ pub fn run_on_first_supported_language_server<T, P>(
1464+ &self,
1465+ view_id: ViewId,
1466+ feature: LanguageServerFeature,
1467+ request_provider: P,
1468+ ) -> Option<T>
1469+ where
1470+ P: Fn(&Client, OffsetEncoding, lsp::Position, lsp::TextDocumentIdentifier) -> Option<T>,
1471+ {
1472+ self.language_servers_with_feature(feature)
1473+ .find_map(|language_server| {
1474+ let offset_encoding = language_server.offset_encoding();
1475+ let pos = self.position(view_id, offset_encoding);
1476+ request_provider(language_server, offset_encoding, pos, self.identifier())
1477+ })
1478+ }
1479+
1463 pub fn diff_handle(&self) -> Option<&DiffHandle> {
1480 pub fn diff_handle(&self) -> Option<&DiffHandle> {
1464 self.diff_handle.as_ref()
1481 self.diff_handle.as_ref()
1465 }
1482 }