summaryrefslogtreecommitdiff
path: root/support/texlab/src/features/reference/string.rs
diff options
context:
space:
mode:
Diffstat (limited to 'support/texlab/src/features/reference/string.rs')
-rw-r--r--support/texlab/src/features/reference/string.rs206
1 files changed, 206 insertions, 0 deletions
diff --git a/support/texlab/src/features/reference/string.rs b/support/texlab/src/features/reference/string.rs
new file mode 100644
index 0000000000..b7dd4e039a
--- /dev/null
+++ b/support/texlab/src/features/reference/string.rs
@@ -0,0 +1,206 @@
+use cancellation::CancellationToken;
+use lsp_types::{Location, ReferenceParams};
+
+use crate::{
+ features::cursor::CursorContext,
+ syntax::{bibtex, CstNode},
+ LineIndexExt,
+};
+
+pub fn find_string_references(
+ context: &CursorContext<ReferenceParams>,
+ cancellation_token: &CancellationToken,
+ items: &mut Vec<Location>,
+) -> Option<()> {
+ let name_text = context
+ .cursor
+ .as_bibtex()
+ .filter(|token| token.kind() == bibtex::WORD)
+ .filter(|token| matches!(token.parent().kind(), bibtex::TOKEN | bibtex::STRING))?
+ .text();
+
+ let document = context.request.main_document();
+ let data = document.data.as_bibtex()?;
+ for node in data.root.descendants() {
+ cancellation_token.result().ok()?;
+ if let Some(name) = bibtex::String::cast(node)
+ .and_then(|string| string.name())
+ .filter(|name| {
+ context.request.params.context.include_declaration && name.text() == name_text
+ })
+ .or_else(|| {
+ bibtex::Token::cast(node)
+ .and_then(|token| token.syntax().first_token())
+ .filter(|name| name.text() == name_text)
+ })
+ {
+ items.push(Location::new(
+ document.uri.as_ref().clone().into(),
+ document.line_index.line_col_lsp_range(name.text_range()),
+ ));
+ }
+ }
+ Some(())
+}
+
+#[cfg(test)]
+mod tests {
+ use indoc::indoc;
+ use lsp_types::Range;
+
+ use crate::{features::testing::FeatureTester, RangeExt};
+
+ use super::*;
+
+ #[test]
+ fn test_definition() {
+ let tester = FeatureTester::builder()
+ .files(vec![(
+ "main.bib",
+ indoc! { r#"
+ @string{foo = {Foo}}
+ @string{bar = {Bar}}
+ @article{baz, author = foo}
+ "# },
+ )])
+ .main("main.bib")
+ .line(2)
+ .character(24)
+ .build();
+ let uri = tester.uri("main.bib");
+
+ let mut actual_references = Vec::new();
+ let request = tester.reference();
+ let context = CursorContext::new(request);
+ find_string_references(&context, CancellationToken::none(), &mut actual_references);
+
+ let expected_references = vec![Location::new(
+ uri.as_ref().clone().into(),
+ Range::new_simple(2, 23, 2, 26),
+ )];
+ assert_eq!(actual_references, expected_references);
+ }
+
+ #[test]
+ fn test_definition_include_declaration() {
+ let tester = FeatureTester::builder()
+ .files(vec![(
+ "main.bib",
+ indoc! { r#"
+ @string{foo = {Foo}}
+ @string{bar = {Bar}}
+ @article{baz, author = foo}
+ "# },
+ )])
+ .main("main.bib")
+ .line(2)
+ .character(24)
+ .include_declaration(true)
+ .build();
+ let uri = tester.uri("main.bib");
+
+ let mut actual_references = Vec::new();
+ let request = tester.reference();
+ let context = CursorContext::new(request);
+ find_string_references(&context, CancellationToken::none(), &mut actual_references);
+
+ let expected_references = vec![
+ Location::new(uri.as_ref().clone().into(), Range::new_simple(0, 8, 0, 11)),
+ Location::new(uri.as_ref().clone().into(), Range::new_simple(2, 23, 2, 26)),
+ ];
+ assert_eq!(actual_references, expected_references);
+ }
+
+ #[test]
+ fn test_reference() {
+ let tester = FeatureTester::builder()
+ .files(vec![(
+ "main.bib",
+ indoc! { r#"
+ @string{foo = {Foo}}
+ @string{bar = {Bar}}
+ @article{baz, author = foo}
+ "# },
+ )])
+ .main("main.bib")
+ .line(0)
+ .character(10)
+ .build();
+ let uri = tester.uri("main.bib");
+
+ let mut actual_references = Vec::new();
+ let request = tester.reference();
+ let context = CursorContext::new(request);
+ find_string_references(&context, CancellationToken::none(), &mut actual_references);
+
+ let expected_references = vec![Location::new(
+ uri.as_ref().clone().into(),
+ Range::new_simple(2, 23, 2, 26),
+ )];
+ assert_eq!(actual_references, expected_references);
+ }
+
+ #[test]
+ fn test_reference_include_declaration() {
+ let tester = FeatureTester::builder()
+ .files(vec![(
+ "main.bib",
+ indoc! { r#"
+ @string{foo = {Foo}}
+ @string{bar = {Bar}}
+ @article{baz, author = foo}
+ "# },
+ )])
+ .main("main.bib")
+ .line(0)
+ .character(10)
+ .include_declaration(true)
+ .build();
+ let uri = tester.uri("main.bib");
+
+ let mut actual_references = Vec::new();
+ let request = tester.reference();
+ let context = CursorContext::new(request);
+ find_string_references(&context, CancellationToken::none(), &mut actual_references);
+
+ let expected_references = vec![
+ Location::new(uri.as_ref().clone().into(), Range::new_simple(0, 8, 0, 11)),
+ Location::new(uri.as_ref().clone().into(), Range::new_simple(2, 23, 2, 26)),
+ ];
+ assert_eq!(actual_references, expected_references);
+ }
+
+ #[test]
+ fn test_empty_latex_document() {
+ let request = FeatureTester::builder()
+ .files(vec![("main.tex", "")])
+ .main("main.tex")
+ .line(0)
+ .character(0)
+ .build()
+ .reference();
+
+ let mut actual_references = Vec::new();
+ let context = CursorContext::new(request);
+ find_string_references(&context, CancellationToken::none(), &mut actual_references);
+
+ assert!(actual_references.is_empty());
+ }
+
+ #[test]
+ fn test_empty_bibtex_document() {
+ let request = FeatureTester::builder()
+ .files(vec![("main.bib", "")])
+ .main("main.bib")
+ .line(0)
+ .character(0)
+ .build()
+ .reference();
+
+ let mut actual_references = Vec::new();
+ let context = CursorContext::new(request);
+ find_string_references(&context, CancellationToken::none(), &mut actual_references);
+
+ assert!(actual_references.is_empty());
+ }
+}