summaryrefslogtreecommitdiff
path: root/support/texlab/src/features/definition/string.rs
diff options
context:
space:
mode:
Diffstat (limited to 'support/texlab/src/features/definition/string.rs')
-rw-r--r--support/texlab/src/features/definition/string.rs176
1 files changed, 176 insertions, 0 deletions
diff --git a/support/texlab/src/features/definition/string.rs b/support/texlab/src/features/definition/string.rs
new file mode 100644
index 0000000000..7162c8b722
--- /dev/null
+++ b/support/texlab/src/features/definition/string.rs
@@ -0,0 +1,176 @@
+use cancellation::CancellationToken;
+use lsp_types::{GotoDefinitionParams, LocationLink};
+
+use crate::{
+ features::cursor::CursorContext,
+ syntax::{bibtex, CstNode},
+ LineIndexExt,
+};
+
+pub fn goto_string_definition(
+ context: &CursorContext<GotoDefinitionParams>,
+ cancellation_token: &CancellationToken,
+) -> Option<Vec<LocationLink>> {
+ let main_document = context.request.main_document();
+
+ let data = main_document.data.as_bibtex()?;
+ let name = context
+ .cursor
+ .as_bibtex()
+ .filter(|token| token.kind() == bibtex::WORD)?;
+
+ bibtex::Token::cast(name.parent())?;
+
+ let origin_selection_range = main_document
+ .line_index
+ .line_col_lsp_range(name.text_range());
+
+ for string in data.root.children().filter_map(bibtex::String::cast) {
+ cancellation_token.result().ok()?;
+
+ if let Some(string_name) = string.name().filter(|n| n.text() == name.text()) {
+ return Some(vec![LocationLink {
+ origin_selection_range: Some(origin_selection_range),
+ target_uri: main_document.uri.as_ref().clone().into(),
+ target_selection_range: main_document
+ .line_index
+ .line_col_lsp_range(string_name.text_range()),
+ target_range: main_document
+ .line_index
+ .line_col_lsp_range(string.small_range()),
+ }]);
+ }
+ }
+
+ None
+}
+
+#[cfg(test)]
+mod tests {
+ use indoc::indoc;
+ use lsp_types::Range;
+
+ use crate::{features::testing::FeatureTester, RangeExt};
+
+ use super::*;
+
+ #[test]
+ fn test_empty_latex_document() {
+ let request = FeatureTester::builder()
+ .files(vec![("main.tex", "")])
+ .main("main.tex")
+ .line(0)
+ .character(0)
+ .build()
+ .definition();
+
+ let context = CursorContext::new(request);
+ let actual_links = goto_string_definition(&context, CancellationToken::none());
+
+ assert!(actual_links.is_none());
+ }
+
+ #[test]
+ fn test_empty_bibtex_document() {
+ let request = FeatureTester::builder()
+ .files(vec![("main.bib", "")])
+ .main("main.bib")
+ .line(0)
+ .character(0)
+ .build()
+ .definition();
+
+ let context = CursorContext::new(request);
+ let actual_links = goto_string_definition(&context, CancellationToken::none());
+
+ assert!(actual_links.is_none());
+ }
+
+ #[test]
+ fn test_simple() {
+ let tester = FeatureTester::builder()
+ .files(vec![(
+ "main.bib",
+ indoc! {
+ r#"
+ @string{foo = {bar}}
+ @article{bar, author = foo}
+ "#
+ },
+ )])
+ .main("main.bib")
+ .line(1)
+ .character(24)
+ .build();
+ let target_uri = tester.uri("main.bib").as_ref().clone().into();
+
+ let request = tester.definition();
+ let context = CursorContext::new(request);
+ let actual_links = goto_string_definition(&context, CancellationToken::none()).unwrap();
+
+ let expected_links = vec![LocationLink {
+ origin_selection_range: Some(Range::new_simple(1, 23, 1, 26)),
+ target_uri,
+ 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 concat() {
+ let tester = FeatureTester::builder()
+ .files(vec![(
+ "main.bib",
+ indoc! {
+ r#"
+ @string{foo = {bar}}
+ @article{bar, author = foo # "bar"}
+ "#
+ },
+ )])
+ .main("main.bib")
+ .line(1)
+ .character(24)
+ .build();
+ let target_uri = tester.uri("main.bib").as_ref().clone().into();
+
+ let request = tester.definition();
+ let context = CursorContext::new(request);
+ let actual_links = goto_string_definition(&context, CancellationToken::none()).unwrap();
+
+ let expected_links = vec![LocationLink {
+ origin_selection_range: Some(Range::new_simple(1, 23, 1, 26)),
+ target_uri,
+ 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 tester = FeatureTester::builder()
+ .files(vec![(
+ "main.bib",
+ indoc! {
+ r#"
+ @string{foo = {bar}}
+ @article{bar, author = foo}
+ "#
+ },
+ )])
+ .main("main.bib")
+ .line(1)
+ .character(18)
+ .build();
+
+ let request = tester.definition();
+ let context = CursorContext::new(request);
+ let actual_links = goto_string_definition(&context, CancellationToken::none());
+
+ assert!(actual_links.is_none());
+ }
+}