diff options
Diffstat (limited to 'support/texlab/src/hover/bibtex')
-rw-r--r-- | support/texlab/src/hover/bibtex/entry_type.rs | 116 | ||||
-rw-r--r-- | support/texlab/src/hover/bibtex/field.rs | 111 | ||||
-rw-r--r-- | support/texlab/src/hover/bibtex/mod.rs | 3 | ||||
-rw-r--r-- | support/texlab/src/hover/bibtex/string_reference.rs | 165 |
4 files changed, 395 insertions, 0 deletions
diff --git a/support/texlab/src/hover/bibtex/entry_type.rs b/support/texlab/src/hover/bibtex/entry_type.rs new file mode 100644 index 0000000000..1e009565d7 --- /dev/null +++ b/support/texlab/src/hover/bibtex/entry_type.rs @@ -0,0 +1,116 @@ +use crate::{ + feature::{FeatureProvider, FeatureRequest}, + protocol::{ + Hover, HoverContents, MarkupContent, MarkupKind, RangeExt, TextDocumentPositionParams, + }, + syntax::{SyntaxNode, LANGUAGE_DATA}, +}; +use async_trait::async_trait; + +#[derive(Debug, PartialEq, Eq, Clone, Copy, Default)] +pub struct BibtexEntryTypeHoverProvider; + +#[async_trait] +impl FeatureProvider for BibtexEntryTypeHoverProvider { + type Params = TextDocumentPositionParams; + type Output = Option<Hover>; + + async fn execute<'a>(&'a self, req: &'a FeatureRequest<Self::Params>) -> Self::Output { + let tree = req.current().content.as_bibtex()?; + for entry in tree + .children(tree.root) + .filter_map(|node| tree.as_entry(node)) + { + if entry.ty.range().contains(req.params.position) { + let ty = &entry.ty.text()[1..]; + let docs = LANGUAGE_DATA.entry_type_documentation(ty)?; + return Some(Hover { + contents: HoverContents::Markup(MarkupContent { + kind: MarkupKind::Markdown, + value: docs.into(), + }), + range: Some(entry.ty.range()), + }); + } + } + None + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{feature::FeatureTester, protocol::Range}; + + #[tokio::test] + async fn empty_latex_document() { + let actual_hover = FeatureTester::new() + .file("main.tex", "") + .main("main.tex") + .position(0, 0) + .test_position(BibtexEntryTypeHoverProvider) + .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(BibtexEntryTypeHoverProvider) + .await; + + assert_eq!(actual_hover, None); + } + + #[tokio::test] + async fn known_entry_type() { + let actual_hover = FeatureTester::new() + .file("main.bib", "@article{foo,}") + .main("main.bib") + .position(0, 3) + .test_position(BibtexEntryTypeHoverProvider) + .await + .unwrap(); + + let expected_hover = Hover { + contents: HoverContents::Markup(MarkupContent { + kind: MarkupKind::Markdown, + value: LANGUAGE_DATA + .entry_type_documentation("article") + .unwrap() + .into(), + }), + range: Some(Range::new_simple(0, 0, 0, 8)), + }; + + assert_eq!(actual_hover, expected_hover); + } + + #[tokio::test] + async fn unknown_entry_type() { + let actual_hover = FeatureTester::new() + .file("main.bib", "@foo{bar,}") + .main("main.bib") + .position(0, 3) + .test_position(BibtexEntryTypeHoverProvider) + .await; + + assert_eq!(actual_hover, None); + } + + #[tokio::test] + async fn entry_key() { + let actual_hover = FeatureTester::new() + .file("main.bib", "@article{foo,}") + .main("main.bib") + .position(0, 11) + .test_position(BibtexEntryTypeHoverProvider) + .await; + + assert_eq!(actual_hover, None); + } +} diff --git a/support/texlab/src/hover/bibtex/field.rs b/support/texlab/src/hover/bibtex/field.rs new file mode 100644 index 0000000000..4b00f3364c --- /dev/null +++ b/support/texlab/src/hover/bibtex/field.rs @@ -0,0 +1,111 @@ +use crate::{ + feature::{FeatureProvider, FeatureRequest}, + protocol::{ + Hover, HoverContents, MarkupContent, MarkupKind, RangeExt, TextDocumentPositionParams, + }, + syntax::{SyntaxNode, LANGUAGE_DATA}, +}; +use async_trait::async_trait; + +#[derive(Debug, PartialEq, Eq, Clone, Copy, Default)] +pub struct BibtexFieldHoverProvider; + +#[async_trait] +impl FeatureProvider for BibtexFieldHoverProvider { + type Params = TextDocumentPositionParams; + type Output = Option<Hover>; + + async fn execute<'a>(&'a self, req: &'a FeatureRequest<Self::Params>) -> Self::Output { + let tree = req.current().content.as_bibtex()?; + let name = tree + .find(req.params.position) + .into_iter() + .filter_map(|node| tree.as_field(node)) + .map(|field| &field.name) + .find(|name| name.range().contains(req.params.position))?; + + let docs = LANGUAGE_DATA.field_documentation(name.text())?; + Some(Hover { + contents: HoverContents::Markup(MarkupContent { + kind: MarkupKind::Markdown, + value: docs.into(), + }), + range: Some(name.range()), + }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{feature::FeatureTester, protocol::Range}; + + #[tokio::test] + async fn empty_latex_document() { + let actual_hover = FeatureTester::new() + .file("main.tex", "") + .main("main.tex") + .position(0, 0) + .test_position(BibtexFieldHoverProvider) + .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(BibtexFieldHoverProvider) + .await; + + assert_eq!(actual_hover, None); + } + + #[tokio::test] + async fn known_field() { + let actual_hover = FeatureTester::new() + .file("main.bib", "@article{foo, author = bar}") + .main("main.bib") + .position(0, 15) + .test_position(BibtexFieldHoverProvider) + .await + .unwrap(); + + let expected_hover = Hover { + contents: HoverContents::Markup(MarkupContent { + kind: MarkupKind::Markdown, + value: LANGUAGE_DATA.field_documentation("author").unwrap().into(), + }), + range: Some(Range::new_simple(0, 14, 0, 20)), + }; + + assert_eq!(actual_hover, expected_hover); + } + + #[tokio::test] + async fn unknown_field() { + let actual_hover = FeatureTester::new() + .file("main.bib", "@article{foo, bar = baz}") + .main("main.bib") + .position(0, 15) + .test_position(BibtexFieldHoverProvider) + .await; + + assert_eq!(actual_hover, None); + } + + #[tokio::test] + async fn entry_key() { + let actual_hover = FeatureTester::new() + .file("main.bib", "@article{foo, bar = baz}") + .main("main.bib") + .position(0, 11) + .test_position(BibtexFieldHoverProvider) + .await; + + assert_eq!(actual_hover, None); + } +} diff --git a/support/texlab/src/hover/bibtex/mod.rs b/support/texlab/src/hover/bibtex/mod.rs new file mode 100644 index 0000000000..59ff356fa0 --- /dev/null +++ b/support/texlab/src/hover/bibtex/mod.rs @@ -0,0 +1,3 @@ +pub mod entry_type; +pub mod field; +pub mod string_reference; diff --git a/support/texlab/src/hover/bibtex/string_reference.rs b/support/texlab/src/hover/bibtex/string_reference.rs new file mode 100644 index 0000000000..46724a4f60 --- /dev/null +++ b/support/texlab/src/hover/bibtex/string_reference.rs @@ -0,0 +1,165 @@ +use crate::{ + feature::{FeatureProvider, FeatureRequest}, + protocol::{ + BibtexFormattingOptions, Hover, HoverContents, MarkupContent, MarkupKind, Position, + TextDocumentPositionParams, + }, + syntax::{bibtex, SyntaxNode}, +}; +use async_trait::async_trait; +use petgraph::graph::NodeIndex; + +#[derive(Debug, PartialEq, Eq, Clone, Copy, Default)] +pub struct BibtexStringReferenceHoverProvider; + +#[async_trait] +impl FeatureProvider for BibtexStringReferenceHoverProvider { + type Params = TextDocumentPositionParams; + type Output = Option<Hover>; + + async fn execute<'a>(&'a self, req: &'a FeatureRequest<Self::Params>) -> Self::Output { + let tree = req.current().content.as_bibtex()?; + let reference = Self::find_reference(tree, req.params.position)?; + for string_node in tree.children(tree.root) { + let hover = Self::find_definition(tree, string_node, reference); + if hover.is_some() { + return hover; + } + } + None + } +} + +impl BibtexStringReferenceHoverProvider { + fn find_reference(tree: &bibtex::Tree, pos: Position) -> Option<&bibtex::Token> { + let mut results = tree.find(pos); + results.reverse(); + match ( + &tree.graph[results[0]], + results.get(1).map(|node| &tree.graph[*node]), + ) { + (bibtex::Node::Word(reference), Some(bibtex::Node::Concat(_))) => { + Some(&reference.token) + } + (bibtex::Node::Word(reference), Some(bibtex::Node::Field(_))) => Some(&reference.token), + _ => None, + } + } + + fn find_definition( + tree: &bibtex::Tree, + string_node: NodeIndex, + reference: &bibtex::Token, + ) -> Option<Hover> { + let string = tree.as_string(string_node)?; + if string.name.as_ref()?.text() != reference.text() { + return None; + } + + let options = BibtexFormattingOptions { + line_length: None, + formatter: None, + }; + let text = bibtex::format( + tree, + tree.children(string_node).next()?, + bibtex::FormattingParams { + tab_size: 4, + insert_spaces: true, + options: &options, + }, + ); + Some(Hover { + contents: HoverContents::Markup(MarkupContent { + kind: MarkupKind::PlainText, + value: text, + }), + range: Some(reference.range()), + }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{ + feature::FeatureTester, + protocol::{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(BibtexStringReferenceHoverProvider) + .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(BibtexStringReferenceHoverProvider) + .await; + + assert_eq!(actual_hover, None); + } + + #[tokio::test] + async fn inside_reference() { + let actual_hover = FeatureTester::new() + .file( + "main.bib", + indoc!( + r#" + @string{foo = "Foo"} + @string{bar = "Bar"} + @article{baz, author = bar} + "# + ), + ) + .main("main.bib") + .position(2, 24) + .test_position(BibtexStringReferenceHoverProvider) + .await + .unwrap(); + + let expected_hover = Hover { + contents: HoverContents::Markup(MarkupContent { + kind: MarkupKind::PlainText, + value: "\"Bar\"".into(), + }), + range: Some(Range::new_simple(2, 23, 2, 26)), + }; + + assert_eq!(actual_hover, expected_hover); + } + + #[tokio::test] + async fn outside_reference() { + let actual_hover = FeatureTester::new() + .file( + "main.bib", + indoc!( + r#" + @string{foo = "Foo"} + @string{bar = "Bar"} + @article{baz, author = bar} + "# + ), + ) + .main("main.bib") + .position(2, 20) + .test_position(BibtexStringReferenceHoverProvider) + .await; + + assert_eq!(actual_hover, None); + } +} |