diff options
Diffstat (limited to 'support/texlab/src/hover/latex/citation.rs')
-rw-r--r-- | support/texlab/src/hover/latex/citation.rs | 179 |
1 files changed, 179 insertions, 0 deletions
diff --git a/support/texlab/src/hover/latex/citation.rs b/support/texlab/src/hover/latex/citation.rs new file mode 100644 index 0000000000..8b5468e52e --- /dev/null +++ b/support/texlab/src/hover/latex/citation.rs @@ -0,0 +1,179 @@ +use crate::{ + citeproc::render_citation, + feature::{FeatureProvider, FeatureRequest}, + protocol::{Hover, HoverContents, RangeExt, TextDocumentPositionParams}, + syntax::{bibtex, Span, SyntaxNode}, + workspace::DocumentContent, +}; +use async_trait::async_trait; +use log::warn; + +#[derive(Debug, PartialEq, Eq, Clone, Copy, Default)] +pub struct LatexCitationHoverProvider; + +#[async_trait] +impl FeatureProvider for LatexCitationHoverProvider { + type Params = TextDocumentPositionParams; + type Output = Option<Hover>; + + async fn execute<'a>(&'a self, req: &'a FeatureRequest<Self::Params>) -> Self::Output { + let (tree, src_key, entry) = Self::get_entry(req)?; + if entry.is_comment() { + None + } else { + let key = entry.key.as_ref()?; + match render_citation(&tree, key.text()) { + Some(markdown) => Some(Hover { + contents: HoverContents::Markup(markdown), + range: Some(src_key.range()), + }), + None => { + warn!("Failed to render entry: {}", key.text()); + None + } + } + } + } +} + +impl LatexCitationHoverProvider { + fn get_entry( + req: &FeatureRequest<TextDocumentPositionParams>, + ) -> Option<(&bibtex::Tree, &Span, &bibtex::Entry)> { + let key = Self::get_key(req)?; + for tree in req + .related() + .iter() + .filter_map(|doc| doc.content.as_bibtex()) + { + for entry in tree + .children(tree.root) + .filter_map(|node| tree.as_entry(node)) + { + if let Some(current_key) = &entry.key { + if current_key.text() == key.text { + return Some((tree, key, entry)); + } + } + } + } + None + } + + fn get_key(req: &FeatureRequest<TextDocumentPositionParams>) -> Option<&Span> { + match &req.current().content { + DocumentContent::Latex(table) => table + .citations + .iter() + .flat_map(|citation| citation.keys(&table)) + .find(|key| key.range().contains(req.params.position)) + .map(|token| &token.span), + DocumentContent::Bibtex(tree) => tree + .children(tree.root) + .filter_map(|node| tree.as_entry(node)) + .filter_map(|entry| entry.key.as_ref()) + .find(|key| key.range().contains(req.params.position)) + .map(|token| &token.span), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{ + feature::FeatureTester, + protocol::{MarkupContent, MarkupKind, Range, RangeExt}, + }; + use indoc::indoc; + + #[tokio::test] + async fn empty_latex_document() { + let actual_hover = FeatureTester::new() + .file("main.tex", "") + .main("main.tex") + .position(0, 0) + .test_position(LatexCitationHoverProvider) + .await; + + assert_eq!(actual_hover, None); + } + + #[tokio::test] + async fn empty_bibtex_document() { + let actual_hover = FeatureTester::new() + .file("main.bib", "") + .main("main.bib") + .position(0, 0) + .test_position(LatexCitationHoverProvider) + .await; + + assert_eq!(actual_hover, None); + } + + #[tokio::test] + async fn inside_label() { + let actual_hover = FeatureTester::new() + .file( + "main.bib", + "@article{foo, author = {Foo Bar}, title = {Baz Qux}, year = 1337}", + ) + .file( + "main.tex", + indoc!( + r#" + \addbibresource{main.bib} + \cite{foo} + "# + ), + ) + .main("main.tex") + .position(1, 7) + .test_position(LatexCitationHoverProvider) + .await + .unwrap(); + + let expected_hover = Hover { + contents: HoverContents::Markup(MarkupContent { + kind: MarkupKind::Markdown, + value: "Bar, F. (1337). *Baz Qux*.".into(), + }), + range: Some(Range::new_simple(1, 6, 1, 9)), + }; + + assert_eq!(actual_hover, expected_hover); + } + + #[tokio::test] + async fn inside_entry() { + let actual_hover = FeatureTester::new() + .file( + "main.bib", + "@article{foo, author = {Foo Bar}, title = {Baz Qux}, year = 1337}", + ) + .file( + "main.tex", + indoc!( + r#" + \addbibresource{main.bib} + \cite{foo} + "# + ), + ) + .main("main.bib") + .position(0, 11) + .test_position(LatexCitationHoverProvider) + .await + .unwrap(); + + let expected_hover = Hover { + contents: HoverContents::Markup(MarkupContent { + kind: MarkupKind::Markdown, + value: "Bar, F. (1337). *Baz Qux*.".into(), + }), + range: Some(Range::new_simple(0, 9, 0, 12)), + }; + + assert_eq!(actual_hover, expected_hover); + } +} |