summaryrefslogtreecommitdiff
path: root/support/texlab/src/hover/bibtex
diff options
context:
space:
mode:
Diffstat (limited to 'support/texlab/src/hover/bibtex')
-rw-r--r--support/texlab/src/hover/bibtex/entry_type.rs116
-rw-r--r--support/texlab/src/hover/bibtex/field.rs111
-rw-r--r--support/texlab/src/hover/bibtex/mod.rs3
-rw-r--r--support/texlab/src/hover/bibtex/string_reference.rs165
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);
+ }
+}