diff options
Diffstat (limited to 'support/texlab/src/reference/bibtex_entry.rs')
-rw-r--r-- | support/texlab/src/reference/bibtex_entry.rs | 197 |
1 files changed, 197 insertions, 0 deletions
diff --git a/support/texlab/src/reference/bibtex_entry.rs b/support/texlab/src/reference/bibtex_entry.rs new file mode 100644 index 0000000000..23e09bdbb6 --- /dev/null +++ b/support/texlab/src/reference/bibtex_entry.rs @@ -0,0 +1,197 @@ +use crate::range::RangeExt; +use crate::syntax::*; +use crate::workspace::*; +use futures_boxed::boxed; +use lsp_types::{Location, ReferenceParams}; + +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct BibtexEntryReferenceProvider; + +impl FeatureProvider for BibtexEntryReferenceProvider { + type Params = ReferenceParams; + type Output = Vec<Location>; + + #[boxed] + async fn execute<'a>(&'a self, request: &'a FeatureRequest<ReferenceParams>) -> Vec<Location> { + let mut references = Vec::new(); + if let Some(key) = Self::find_key(request) { + for document in request.related_documents() { + match &document.tree { + SyntaxTree::Latex(tree) => tree + .citations + .iter() + .flat_map(LatexCitation::keys) + .filter(|citation| citation.text() == key) + .map(|citation| { + Location::new(document.uri.clone().into(), citation.range()) + }) + .for_each(|location| references.push(location)), + SyntaxTree::Bibtex(tree) => { + if request.params.context.include_declaration { + for entry in tree.entries() { + if let Some(key_token) = &entry.key { + if key_token.text() == key { + let uri = document.uri.clone(); + let location = Location::new(uri.into(), key_token.range()); + references.push(location); + } + } + } + } + } + } + } + } + references + } +} + +impl BibtexEntryReferenceProvider { + fn find_key(request: &FeatureRequest<ReferenceParams>) -> Option<&str> { + match &request.document().tree { + SyntaxTree::Latex(tree) => tree + .citations + .iter() + .flat_map(LatexCitation::keys) + .find(|key| { + key.range() + .contains(request.params.text_document_position.position) + }) + .map(LatexToken::text), + SyntaxTree::Bibtex(tree) => { + for entry in tree.entries() { + if let Some(key) = &entry.key { + if key + .range() + .contains(request.params.text_document_position.position) + { + return Some(key.text()); + } + } + } + None + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::range::RangeExt; + use lsp_types::{Position, Range}; + + #[test] + fn test_entry() { + let references = test_feature( + BibtexEntryReferenceProvider, + FeatureSpec { + files: vec![ + FeatureSpec::file("foo.bib", "@article{foo, bar = {baz}}"), + FeatureSpec::file("bar.tex", "\\addbibresource{foo.bib}\n\\cite{foo}"), + FeatureSpec::file("baz.tex", "\\cite{foo}"), + ], + main_file: "foo.bib", + position: Position::new(0, 9), + include_declaration: false, + ..FeatureSpec::default() + }, + ); + assert_eq!( + references, + vec![Location::new( + FeatureSpec::uri("bar.tex"), + Range::new_simple(1, 6, 1, 9) + )] + ); + } + + #[test] + fn test_entry_include_declaration() { + let references = test_feature( + BibtexEntryReferenceProvider, + FeatureSpec { + files: vec![ + FeatureSpec::file("foo.bib", "@article{foo, bar = {baz}}"), + FeatureSpec::file("bar.tex", "\\addbibresource{foo.bib}\n\\cite{foo}"), + FeatureSpec::file("baz.tex", "\\cite{foo}"), + ], + main_file: "foo.bib", + position: Position::new(0, 9), + include_declaration: true, + ..FeatureSpec::default() + }, + ); + assert_eq!( + references, + vec![ + Location::new(FeatureSpec::uri("foo.bib"), Range::new_simple(0, 9, 0, 12)), + Location::new(FeatureSpec::uri("bar.tex"), Range::new_simple(1, 6, 1, 9)), + ] + ); + } + + #[test] + fn test_citation() { + let references = test_feature( + BibtexEntryReferenceProvider, + FeatureSpec { + files: vec![ + FeatureSpec::file("foo.bib", "@article{foo, bar = {baz}}"), + FeatureSpec::file("bar.tex", "\\addbibresource{foo.bib}\n\\cite{foo}"), + FeatureSpec::file("baz.tex", "\\cite{foo}"), + ], + main_file: "bar.tex", + position: Position::new(1, 8), + include_declaration: false, + ..FeatureSpec::default() + }, + ); + assert_eq!( + references, + vec![Location::new( + FeatureSpec::uri("bar.tex"), + Range::new_simple(1, 6, 1, 9) + )] + ); + } + + #[test] + fn test_citation_include_declaration() { + let references = test_feature( + BibtexEntryReferenceProvider, + FeatureSpec { + files: vec![ + FeatureSpec::file("foo.bib", "@article{foo, bar = {baz}}"), + FeatureSpec::file("bar.tex", "\\addbibresource{foo.bib}\n\\cite{foo}"), + FeatureSpec::file("baz.tex", "\\cite{foo}"), + ], + main_file: "bar.tex", + position: Position::new(1, 9), + include_declaration: true, + ..FeatureSpec::default() + }, + ); + assert_eq!( + references, + vec![ + Location::new(FeatureSpec::uri("bar.tex"), Range::new_simple(1, 6, 1, 9)), + Location::new(FeatureSpec::uri("foo.bib"), Range::new_simple(0, 9, 0, 12)), + ] + ); + } + + #[test] + fn test_empty() { + let references = test_feature( + BibtexEntryReferenceProvider, + FeatureSpec { + files: vec![FeatureSpec::file("foo.tex", "")], + main_file: "foo.tex", + position: Position::new(0, 0), + ..FeatureSpec::default() + }, + ); + assert!(references.is_empty()); + } +} |