Remove boilerplate in the goto methods by generically composing functions

pull/2507/head
Philipp Mildenberger 1 year ago
parent d963050621
commit 60a6af1fea

@ -6,7 +6,7 @@ use helix_lsp::{
NumberOrString, NumberOrString,
}, },
util::{diagnostic_to_lsp_diagnostic, lsp_range_to_range, range_to_lsp_range}, util::{diagnostic_to_lsp_diagnostic, lsp_range_to_range, range_to_lsp_range},
OffsetEncoding, Client, OffsetEncoding,
}; };
use serde_json::Value; use serde_json::Value;
use tokio_stream::StreamExt; use tokio_stream::StreamExt;
@ -1028,151 +1028,90 @@ fn to_locations(definitions: Option<lsp::GotoDefinitionResponse>) -> Vec<lsp::Lo
} }
} }
// TODO find a way to reduce boilerplate of all the goto functions, without unnecessary complexity... fn goto_single_impl<P, F>(cx: &mut Context, feature: LanguageServerFeature, request_provider: P)
pub fn goto_declaration(cx: &mut Context) { where
P: Fn(&Client, lsp::Position, lsp::TextDocumentIdentifier) -> Option<F>,
F: Future<Output = helix_lsp::Result<serde_json::Value>> + 'static + Send,
{
let (view, doc) = current!(cx.editor); let (view, doc) = current!(cx.editor);
let future_offset_encoding = doc if let Some((future, offset_encoding)) =
.language_servers_with_feature(LanguageServerFeature::GotoDeclaration) doc.run_on_first_supported_language_server(view.id, feature, |ls, encoding, pos, doc_id| {
.find_map(|language_server| { Some((request_provider(ls, pos, doc_id)?, encoding))
let offset_encoding = language_server.offset_encoding(); })
let pos = doc.position(view.id, offset_encoding); {
let future = language_server.goto_declaration(doc.identifier(), pos, None)?; cx.callback(
Some((future, offset_encoding)) future,
}); move |editor, compositor, response: Option<lsp::GotoDefinitionResponse>| {
let (future, offset_encoding) = match future_offset_encoding { let items = to_locations(response);
Some(future_offset_encoding) => future_offset_encoding, goto_impl(editor, compositor, items, offset_encoding);
None => { },
cx.editor );
.set_error("No language server supports goto-declaration"); } else {
return; cx.editor.set_error("No language server supports {feature}");
} }
}; }
cx.callback( pub fn goto_declaration(cx: &mut Context) {
future, goto_single_impl(
move |editor, compositor, response: Option<lsp::GotoDefinitionResponse>| { cx,
let items = to_locations(response); LanguageServerFeature::GotoDeclaration,
goto_impl(editor, compositor, items, offset_encoding); |ls, pos, doc_id| ls.goto_declaration(doc_id, pos, None),
},
); );
} }
pub fn goto_definition(cx: &mut Context) { pub fn goto_definition(cx: &mut Context) {
let (view, doc) = current!(cx.editor); goto_single_impl(
let future_offset_encoding = doc cx,
.language_servers_with_feature(LanguageServerFeature::GotoDefinition) LanguageServerFeature::GotoDefinition,
.find_map(|language_server| { |ls, pos, doc_id| ls.goto_definition(doc_id, pos, None),
let offset_encoding = language_server.offset_encoding();
let pos = doc.position(view.id, offset_encoding);
let future = language_server.goto_definition(doc.identifier(), pos, None)?;
Some((future, offset_encoding))
});
let (future, offset_encoding) = match future_offset_encoding {
Some(future_offset_encoding) => future_offset_encoding,
None => {
cx.editor
.set_error("No language server supports goto-definition");
return;
}
};
cx.callback(
future,
move |editor, compositor, response: Option<lsp::GotoDefinitionResponse>| {
let items = to_locations(response);
goto_impl(editor, compositor, items, offset_encoding);
},
); );
} }
pub fn goto_type_definition(cx: &mut Context) { pub fn goto_type_definition(cx: &mut Context) {
let (view, doc) = current!(cx.editor); goto_single_impl(
let future_offset_encoding = doc cx,
.language_servers_with_feature(LanguageServerFeature::GotoTypeDefinition) LanguageServerFeature::GotoTypeDefinition,
.find_map(|language_server| { |ls, pos, doc_id| ls.goto_type_definition(doc_id, pos, None),
let offset_encoding = language_server.offset_encoding();
let pos = doc.position(view.id, offset_encoding);
let future = language_server.goto_type_definition(doc.identifier(), pos, None)?;
Some((future, offset_encoding))
});
let (future, offset_encoding) = match future_offset_encoding {
Some(future_offset_encoding) => future_offset_encoding,
None => {
cx.editor
.set_error("No language server supports goto-type-definition");
return;
}
};
cx.callback(
future,
move |editor, compositor, response: Option<lsp::GotoDefinitionResponse>| {
let items = to_locations(response);
goto_impl(editor, compositor, items, offset_encoding);
},
); );
} }
pub fn goto_implementation(cx: &mut Context) { pub fn goto_implementation(cx: &mut Context) {
let (view, doc) = current!(cx.editor); goto_single_impl(
let future_offset_encoding = doc cx,
.language_servers_with_feature(LanguageServerFeature::GotoImplementation) LanguageServerFeature::GotoImplementation,
.find_map(|language_server| { |ls, pos, doc_id| ls.goto_implementation(doc_id, pos, None),
let offset_encoding = language_server.offset_encoding();
let pos = doc.position(view.id, offset_encoding);
let future = language_server.goto_implementation(doc.identifier(), pos, None)?;
Some((future, offset_encoding))
});
let (future, offset_encoding) = match future_offset_encoding {
Some(future_offset_encoding) => future_offset_encoding,
None => {
cx.editor
.set_error("No language server supports goto-implementation");
return;
}
};
cx.callback(
future,
move |editor, compositor, response: Option<lsp::GotoDefinitionResponse>| {
let items = to_locations(response);
goto_impl(editor, compositor, items, offset_encoding);
},
); );
} }
pub fn goto_reference(cx: &mut Context) { pub fn goto_reference(cx: &mut Context) {
let config = cx.editor.config(); let config = cx.editor.config();
let (view, doc) = current!(cx.editor); let (view, doc) = current!(cx.editor);
let future_offset_encoding = doc if let Some((future, offset_encoding)) = doc.run_on_first_supported_language_server(
.language_servers_with_feature(LanguageServerFeature::GotoReference) view.id,
.find_map(|language_server| { LanguageServerFeature::GotoReference,
let offset_encoding = language_server.offset_encoding(); |ls, encoding, pos, doc_id| {
let pos = doc.position(view.id, offset_encoding); Some((
let future = language_server.goto_reference( ls.goto_reference(
doc.identifier(), doc_id,
pos, pos,
config.lsp.goto_reference_include_declaration, config.lsp.goto_reference_include_declaration,
None, None,
)?; )?,
Some((future, offset_encoding)) encoding,
}); ))
let (future, offset_encoding) = match future_offset_encoding {
Some(future_offset_encoding) => future_offset_encoding,
None => {
cx.editor
.set_error("No language server supports goto-reference");
return;
}
};
cx.callback(
future,
move |editor, compositor, response: Option<Vec<lsp::Location>>| {
let items = response.unwrap_or_default();
goto_impl(editor, compositor, items, offset_encoding);
}, },
); ) {
cx.callback(
future,
move |editor, compositor, response: Option<Vec<lsp::Location>>| {
let items = response.unwrap_or_default();
goto_impl(editor, compositor, items, offset_encoding);
},
);
} else {
cx.editor
.set_error("No language server supports goto-reference");
}
} }
#[derive(PartialEq, Eq, Clone, Copy)] #[derive(PartialEq, Eq, Clone, Copy)]

@ -580,7 +580,7 @@ where
*mut_ref = f(mem::take(mut_ref)); *mut_ref = f(mem::take(mut_ref));
} }
use helix_lsp::lsp; use helix_lsp::{lsp, Client, OffsetEncoding};
use url::Url; use url::Url;
impl Document { impl Document {
@ -1460,6 +1460,23 @@ impl Document {
self.language_servers().any(|l| l.id() == id) self.language_servers().any(|l| l.id() == id)
} }
pub fn run_on_first_supported_language_server<T, P>(
&self,
view_id: ViewId,
feature: LanguageServerFeature,
request_provider: P,
) -> Option<T>
where
P: Fn(&Client, OffsetEncoding, lsp::Position, lsp::TextDocumentIdentifier) -> Option<T>,
{
self.language_servers_with_feature(feature)
.find_map(|language_server| {
let offset_encoding = language_server.offset_encoding();
let pos = self.position(view_id, offset_encoding);
request_provider(language_server, offset_encoding, pos, self.identifier())
})
}
pub fn diff_handle(&self) -> Option<&DiffHandle> { pub fn diff_handle(&self) -> Option<&DiffHandle> {
self.diff_handle.as_ref() self.diff_handle.as_ref()
} }

Loading…
Cancel
Save