diff options
Diffstat (limited to 'support/texlab/src/definition/bibtex_string.rs')
-rw-r--r-- | support/texlab/src/definition/bibtex_string.rs | 225 |
1 files changed, 132 insertions, 93 deletions
diff --git a/support/texlab/src/definition/bibtex_string.rs b/support/texlab/src/definition/bibtex_string.rs index 0e4f643153..a1fe5132c0 100644 --- a/support/texlab/src/definition/bibtex_string.rs +++ b/support/texlab/src/definition/bibtex_string.rs @@ -1,20 +1,23 @@ -use crate::syntax::*; -use crate::workspace::*; -use futures_boxed::boxed; -use lsp_types::{LocationLink, Position, TextDocumentPositionParams}; +use crate::{ + feature::{FeatureProvider, FeatureRequest}, + protocol::{LocationLink, Position, TextDocumentPositionParams, Uri}, + syntax::{bibtex, SyntaxNode}, + workspace::DocumentContent, +}; +use async_trait::async_trait; -#[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[derive(Debug, PartialEq, Eq, Clone, Copy, Default)] pub struct BibtexStringDefinitionProvider; +#[async_trait] impl FeatureProvider for BibtexStringDefinitionProvider { type Params = TextDocumentPositionParams; type Output = Vec<LocationLink>; - #[boxed] - async fn execute<'a>(&'a self, request: &'a FeatureRequest<Self::Params>) -> Self::Output { - if let SyntaxTree::Bibtex(tree) = &request.document().tree { - if let Some(reference) = Self::find_reference(tree, request.params.position) { - return Self::find_definitions(&request.view.document.uri, tree, reference); + async fn execute<'a>(&'a self, req: &'a FeatureRequest<Self::Params>) -> Self::Output { + if let DocumentContent::Bibtex(tree) = &req.current().content { + if let Some(reference) = Self::find_reference(tree, req.params.position) { + return Self::find_definitions(&req.current().uri, tree, reference); } } Vec::new() @@ -22,31 +25,36 @@ impl FeatureProvider for BibtexStringDefinitionProvider { } impl BibtexStringDefinitionProvider { - fn find_reference(tree: &BibtexSyntaxTree, position: Position) -> Option<&BibtexToken> { - let mut nodes = tree.find(position); + fn find_reference(tree: &bibtex::Tree, pos: Position) -> Option<&bibtex::Token> { + let mut nodes = tree.find(pos); nodes.reverse(); - match (&nodes[0], &nodes.get(1)) { - (BibtexNode::Word(word), Some(BibtexNode::Field(_))) - | (BibtexNode::Word(word), Some(BibtexNode::Concat(_))) => Some(&word.token), + match ( + &tree.graph[nodes[0]], + nodes.get(1).map(|node| &tree.graph[*node]), + ) { + (bibtex::Node::Word(word), Some(bibtex::Node::Field(_))) + | (bibtex::Node::Word(word), Some(bibtex::Node::Concat(_))) => Some(&word.token), _ => None, } } fn find_definitions( uri: &Uri, - tree: &BibtexSyntaxTree, - reference: &BibtexToken, + tree: &bibtex::Tree, + reference: &bibtex::Token, ) -> Vec<LocationLink> { let mut links = Vec::new(); - for string in tree.strings() { - if let Some(name) = &string.name { - if name.text() == reference.text() { - links.push(LocationLink { - origin_selection_range: Some(reference.range()), - target_uri: uri.clone().into(), - target_range: string.range(), - target_selection_range: name.range(), - }); + for node in tree.children(tree.root) { + if let Some(string) = tree.as_string(node) { + if let Some(name) = &string.name { + if name.text() == reference.text() { + links.push(LocationLink { + origin_selection_range: Some(reference.range()), + target_uri: uri.clone().into(), + target_range: string.range(), + target_selection_range: name.range(), + }); + } } } } @@ -57,76 +65,107 @@ impl BibtexStringDefinitionProvider { #[cfg(test)] mod tests { use super::*; - use crate::range::RangeExt; - use lsp_types::{Position, Range}; - - #[test] - fn test_simple() { - let links = test_feature( - BibtexStringDefinitionProvider, - FeatureSpec { - files: vec![FeatureSpec::file( - "foo.bib", - "@string{foo = {bar}}\n@article{bar, author = foo}", - )], - main_file: "foo.bib", - position: Position::new(1, 24), - ..FeatureSpec::default() - }, - ); - - assert_eq!( - links, - vec![LocationLink { - origin_selection_range: Some(Range::new_simple(1, 23, 1, 26)), - target_uri: FeatureSpec::uri("foo.bib"), - target_range: Range::new_simple(0, 0, 0, 20), - target_selection_range: Range::new_simple(0, 8, 0, 11) - }] - ); + use crate::{ + feature::FeatureTester, + protocol::{Range, RangeExt}, + }; + use indoc::indoc; + + #[tokio::test] + async fn empty_latex_document() { + let actual_links = FeatureTester::new() + .file("main.tex", "") + .main("main.tex") + .position(0, 0) + .test_position(BibtexStringDefinitionProvider) + .await; + + assert!(actual_links.is_empty()); + } + + #[tokio::test] + async fn empty_bibtex_document() { + let actual_links = FeatureTester::new() + .file("main.bib", "") + .main("main.bib") + .position(0, 0) + .test_position(BibtexStringDefinitionProvider) + .await; + + assert!(actual_links.is_empty()); + } + + #[tokio::test] + async fn simple() { + let actual_links = FeatureTester::new() + .file( + "main.bib", + indoc!( + r#" + @string{foo = {bar}} + @article{bar, author = foo} + "# + ), + ) + .main("main.bib") + .position(1, 24) + .test_position(BibtexStringDefinitionProvider) + .await; + + let expected_links = vec![LocationLink { + origin_selection_range: Some(Range::new_simple(1, 23, 1, 26)), + target_uri: FeatureTester::uri("main.bib").into(), + target_range: Range::new_simple(0, 0, 0, 20), + target_selection_range: Range::new_simple(0, 8, 0, 11), + }]; + + assert_eq!(actual_links, expected_links); } - #[test] - fn test_concat() { - let links = test_feature( - BibtexStringDefinitionProvider, - FeatureSpec { - files: vec![FeatureSpec::file( - "foo.bib", - "@string{foo = {bar}}\n@article{bar, author = foo # \"bar\"}", - )], - main_file: "foo.bib", - position: Position::new(1, 24), - ..FeatureSpec::default() - }, - ); - - assert_eq!( - links, - vec![LocationLink { - origin_selection_range: Some(Range::new_simple(1, 23, 1, 26)), - target_uri: FeatureSpec::uri("foo.bib"), - target_range: Range::new_simple(0, 0, 0, 20), - target_selection_range: Range::new_simple(0, 8, 0, 11) - }] - ); + #[tokio::test] + async fn concat() { + let actual_links = FeatureTester::new() + .file( + "main.bib", + indoc!( + r#" + @string{foo = {bar}} + @article{bar, author = foo # "bar"} + "# + ), + ) + .main("main.bib") + .position(1, 24) + .test_position(BibtexStringDefinitionProvider) + .await; + + let expected_links = vec![LocationLink { + origin_selection_range: Some(Range::new_simple(1, 23, 1, 26)), + target_uri: FeatureTester::uri("main.bib").into(), + target_range: Range::new_simple(0, 0, 0, 20), + target_selection_range: Range::new_simple(0, 8, 0, 11), + }]; + + assert_eq!(actual_links, expected_links); } - #[test] - fn test_field() { - let links = test_feature( - BibtexStringDefinitionProvider, - FeatureSpec { - files: vec![FeatureSpec::file( - "foo.bib", - "@string{foo = {bar}}\n@article{bar, author = foo}", - )], - main_file: "foo.bib", - position: Position::new(1, 18), - ..FeatureSpec::default() - }, - ); - - assert!(links.is_empty()); + #[tokio::test] + async fn field() { + let actual_links = FeatureTester::new() + .file( + "main.bib", + indoc!( + r#" + @string{foo = {bar}} + @article{bar, author = foo} + "# + ), + ) + .main("main.bib") + .position(1, 18) + .test_position(BibtexStringDefinitionProvider) + .await; + + assert!(actual_links.is_empty()); } } |