@@ -6,7 +6,7 @@ use helix_lsp::{
66 NumberOrString,
77 },
88 util::{diagnostic_to_lsp_diagnostic, lsp_range_to_range, range_to_lsp_range},
9− OffsetEncoding,
9+ Client, OffsetEncoding,
1010};
1111use serde_json::Value;
1212use tokio_stream::StreamExt;
@@ -1028,151 +1028,90 @@ fn to_locations(definitions: Option<lsp::GotoDefinitionResponse>) -> Vec<lsp::Lo
10281028 }
10291029}
10301030
1031−// TODO find a way to reduce boilerplate of all the goto functions, without unnecessary complexity...
1032−pub fn goto_declaration(cx: &mut Context) {
1031+fn goto_single_impl<P, F>(cx: &mut Context, feature: LanguageServerFeature, request_provider: P)
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+{
10331036 let (view, doc) = current!(cx.editor);
1034− let future_offset_encoding = doc
1035− .language_servers_with_feature(LanguageServerFeature::GotoDeclaration)
1036− .find_map(|language_server| {
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− });
1042− let (future, offset_encoding) = match future_offset_encoding {
1043− Some(future_offset_encoding) => future_offset_encoding,
1044− None => {
1045− cx.editor
1046− .set_error("No language server supports goto-declaration");
1047− return;
1048− }
1049− };
1037+ if let Some((future, offset_encoding)) =
1038+ doc.run_on_first_supported_language_server(view.id, feature, |ls, encoding, pos, doc_id| {
1039+ Some((request_provider(ls, pos, doc_id)?, encoding))
1040+ })
1041+ {
1042+ cx.callback(
1043+ future,
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 {
1050+ cx.editor.set_error("No language server supports {feature}");
1051+ }
1052+}
10501053
1051− cx.callback(
1052− future,
1053− move |editor, compositor, response: Option<lsp::GotoDefinitionResponse>| {
1054− let items = to_locations(response);
1055− goto_impl(editor, compositor, items, offset_encoding);
1056− },
1054+pub fn goto_declaration(cx: &mut Context) {
1055+ goto_single_impl(
1056+ cx,
1057+ LanguageServerFeature::GotoDeclaration,
1058+ |ls, pos, doc_id| ls.goto_declaration(doc_id, pos, None),
10571059 );
10581060}
10591061
10601062pub fn goto_definition(cx: &mut Context) {
1061− let (view, doc) = current!(cx.editor);
1062− let future_offset_encoding = doc
1063− .language_servers_with_feature(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)?;
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− },
1063+ goto_single_impl(
1064+ cx,
1065+ LanguageServerFeature::GotoDefinition,
1066+ |ls, pos, doc_id| ls.goto_definition(doc_id, pos, None),
10851067 );
10861068}
10871069
10881070pub fn goto_type_definition(cx: &mut Context) {
1089− let (view, doc) = current!(cx.editor);
1090− let future_offset_encoding = doc
1091− .language_servers_with_feature(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)?;
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− },
1071+ goto_single_impl(
1072+ cx,
1073+ LanguageServerFeature::GotoTypeDefinition,
1074+ |ls, pos, doc_id| ls.goto_type_definition(doc_id, pos, None),
11131075 );
11141076}
11151077
11161078pub fn goto_implementation(cx: &mut Context) {
1117− let (view, doc) = current!(cx.editor);
1118− let future_offset_encoding = doc
1119− .language_servers_with_feature(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)?;
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− },
1079+ goto_single_impl(
1080+ cx,
1081+ LanguageServerFeature::GotoImplementation,
1082+ |ls, pos, doc_id| ls.goto_implementation(doc_id, pos, None),
11411083 );
11421084}
11431085
11441086pub fn goto_reference(cx: &mut Context) {
11451087 let config = cx.editor.config();
11461088 let (view, doc) = current!(cx.editor);
1147− let future_offset_encoding = doc
1148− .language_servers_with_feature(LanguageServerFeature::GotoReference)
1149− .find_map(|language_server| {
1150− let offset_encoding = language_server.offset_encoding();
1151− let pos = doc.position(view.id, offset_encoding);
1152− let future = language_server.goto_reference(
1153− doc.identifier(),
1154− pos,
1155− config.lsp.goto_reference_include_declaration,
1156− None,
1157− )?;
1158− Some((future, offset_encoding))
1159− });
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);
1089+ if let Some((future, offset_encoding)) = doc.run_on_first_supported_language_server(
1090+ view.id,
1091+ LanguageServerFeature::GotoReference,
1092+ |ls, encoding, pos, doc_id| {
1093+ Some((
1094+ ls.goto_reference(
1095+ doc_id,
1096+ pos,
1097+ config.lsp.goto_reference_include_declaration,
1098+ None,
1099+ )?,
1100+ encoding,
1101+ ))
11741102 },
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+ }
11761115}
11771116
11781117#[derive(PartialEq, Eq, Clone, Copy)]