summaryrefslogtreecommitdiff
path: root/support/texlab/tests/integration
diff options
context:
space:
mode:
Diffstat (limited to 'support/texlab/tests/integration')
-rw-r--r--support/texlab/tests/integration/completion.rs1227
-rw-r--r--support/texlab/tests/integration/definition.rs482
-rw-r--r--support/texlab/tests/integration/document_symbol.rs424
-rw-r--r--support/texlab/tests/integration/folding.rs126
-rw-r--r--support/texlab/tests/integration/highlight.rs83
-rw-r--r--support/texlab/tests/integration/hover.rs86
-rw-r--r--support/texlab/tests/integration/issues.rs350
-rw-r--r--support/texlab/tests/integration/link.rs173
-rw-r--r--support/texlab/tests/integration/main.rs12
-rw-r--r--support/texlab/tests/integration/prepare_rename.rs151
-rw-r--r--support/texlab/tests/integration/reference.rs150
-rw-r--r--support/texlab/tests/integration/rename.rs236
-rw-r--r--support/texlab/tests/integration/workspace_symbol.rs228
13 files changed, 3728 insertions, 0 deletions
diff --git a/support/texlab/tests/integration/completion.rs b/support/texlab/tests/integration/completion.rs
new file mode 100644
index 0000000000..4b3b4ecd83
--- /dev/null
+++ b/support/texlab/tests/integration/completion.rs
@@ -0,0 +1,1227 @@
+#[cfg(feature = "citation")]
+use texlab::protocol::{MarkupContent, MarkupKind};
+
+use indoc::indoc;
+use itertools::Itertools;
+use texlab::{
+ protocol::{CompletionItem, CompletionTextEdit, Documentation, Range, RangeExt, TextEdit},
+ test::{TestBed, TestBedBuilder, TestLspClient, PULL_CAPABILITIES},
+};
+
+async fn run_item(
+ test_bed: &TestBed,
+ relative_path: &str,
+ line: u64,
+ character: u64,
+ label: &str,
+) -> CompletionItem {
+ let item = test_bed
+ .completion(relative_path, line, character)
+ .await
+ .unwrap()
+ .into_iter()
+ .find(|item| item.label == label)
+ .unwrap();
+
+ test_bed.client.completion_resolve(item).await.unwrap()
+}
+
+async fn run_list(
+ test_bed: &TestBed,
+ relative_path: &str,
+ line: u64,
+ character: u64,
+) -> Vec<String> {
+ test_bed
+ .completion(relative_path, line, character)
+ .await
+ .unwrap()
+ .into_iter()
+ .map(|item| item.label)
+ .sorted()
+ .collect()
+}
+
+fn verify_text_edit(
+ item: &CompletionItem,
+ start_line: u64,
+ start_character: u64,
+ end_line: u64,
+ end_character: u64,
+ text: &str,
+) {
+ assert_eq!(
+ *item.text_edit.as_ref().unwrap(),
+ CompletionTextEdit::Edit(TextEdit::new(
+ Range::new_simple(start_line, start_character, end_line, end_character),
+ text.into()
+ ))
+ );
+}
+
+fn verify_detail(item: &CompletionItem, detail: &str) {
+ assert_eq!(item.detail.as_ref().unwrap(), detail);
+}
+
+#[tokio::test]
+async fn empty_latex_document() {
+ let mut test_bed = TestBedBuilder::new().file("main.tex", "").build().await;
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+ test_bed.open("main.tex").await;
+
+ let actual_items = test_bed.completion("main.tex", 0, 0).await.unwrap();
+
+ test_bed.shutdown().await;
+
+ assert!(actual_items.is_empty());
+}
+
+#[tokio::test]
+async fn empty_bibtex_document() {
+ let mut test_bed = TestBedBuilder::new().file("main.bib", "").build().await;
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+ test_bed.open("main.bib").await;
+
+ let actual_items = test_bed.completion("main.bib", 0, 0).await.unwrap();
+
+ test_bed.shutdown().await;
+
+ assert!(actual_items.is_empty());
+}
+
+#[tokio::test]
+async fn bibtex_comment() {
+ let mut test_bed = TestBedBuilder::new().file("main.bib", "foo").build().await;
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+ test_bed.open("main.bib").await;
+
+ let actual_items = test_bed.completion("main.bib", 0, 2).await.unwrap();
+
+ test_bed.shutdown().await;
+
+ assert!(actual_items.is_empty());
+}
+
+#[tokio::test]
+async fn bibtex_command_incomplete_entry() {
+ let mut test_bed = TestBedBuilder::new()
+ .file(
+ "main.bib",
+ indoc!(
+ r#"
+ @article{foo,
+ author = {\LaT
+ }
+ "#
+ ),
+ )
+ .build()
+ .await;
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+ test_bed.open("main.bib").await;
+
+ let actual_item = run_item(&test_bed, "main.bib", 1, 18, "LaTeX").await;
+
+ test_bed.shutdown().await;
+
+ verify_detail(&actual_item, "built-in");
+ verify_text_edit(&actual_item, 1, 15, 1, 18, "LaTeX");
+}
+
+#[tokio::test]
+async fn bibtex_command_complete_entry() {
+ let mut test_bed = TestBedBuilder::new()
+ .file(
+ "main.bib",
+ indoc!(
+ r#"
+ @article{foo,
+ author = {\LaT}
+ }
+ "#
+ ),
+ )
+ .build()
+ .await;
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+ test_bed.open("main.bib").await;
+
+ let actual_item = run_item(&test_bed, "main.bib", 1, 18, "LaTeX").await;
+
+ test_bed.shutdown().await;
+
+ verify_detail(&actual_item, "built-in");
+ verify_text_edit(&actual_item, 1, 15, 1, 18, "LaTeX");
+}
+
+#[tokio::test]
+async fn bibtex_type_empty() {
+ let mut test_bed = TestBedBuilder::new()
+ .file(
+ "main.bib",
+ indoc!(
+ r#"
+ @
+ "#
+ ),
+ )
+ .build()
+ .await;
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+ test_bed.open("main.bib").await;
+
+ let actual_item = run_item(&test_bed, "main.bib", 0, 1, "article").await;
+
+ test_bed.shutdown().await;
+
+ assert!(actual_item.documentation.is_some());
+ verify_text_edit(&actual_item, 0, 1, 0, 1, "article");
+}
+
+#[tokio::test]
+async fn bibtex_type_incomplete() {
+ let mut test_bed = TestBedBuilder::new()
+ .file(
+ "main.bib",
+ indoc!(
+ r#"
+ @art
+ "#
+ ),
+ )
+ .build()
+ .await;
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+ test_bed.open("main.bib").await;
+
+ let actual_item = run_item(&test_bed, "main.bib", 0, 1, "article").await;
+
+ test_bed.shutdown().await;
+
+ assert!(actual_item.documentation.is_some());
+ verify_text_edit(&actual_item, 0, 1, 0, 4, "article");
+}
+
+#[tokio::test]
+async fn bibtex_type_complete() {
+ let mut test_bed = TestBedBuilder::new()
+ .file(
+ "main.bib",
+ indoc!(
+ r#"
+ @article
+ "#
+ ),
+ )
+ .build()
+ .await;
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+ test_bed.open("main.bib").await;
+
+ let actual_item = run_item(&test_bed, "main.bib", 0, 1, "article").await;
+
+ test_bed.shutdown().await;
+
+ assert!(actual_item.documentation.is_some());
+ verify_text_edit(&actual_item, 0, 1, 0, 8, "article");
+}
+
+#[tokio::test]
+async fn bibtex_field_incomplete_entry() {
+ let mut test_bed = TestBedBuilder::new()
+ .file(
+ "main.bib",
+ indoc!(
+ r#"
+ @article{foo,
+ titl
+ "#
+ ),
+ )
+ .build()
+ .await;
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+ test_bed.open("main.bib").await;
+
+ let actual_item = run_item(&test_bed, "main.bib", 1, 6, "title").await;
+
+ test_bed.shutdown().await;
+
+ assert!(actual_item.documentation.is_some());
+ verify_text_edit(&actual_item, 1, 4, 1, 8, "title");
+}
+
+#[tokio::test]
+async fn bibtex_field_complete_entry() {
+ let mut test_bed = TestBedBuilder::new()
+ .file(
+ "main.bib",
+ indoc!(
+ r#"
+ @article{foo,
+ title = {}
+ }
+ "#
+ ),
+ )
+ .build()
+ .await;
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+ test_bed.open("main.bib").await;
+
+ let actual_item = run_item(&test_bed, "main.bib", 1, 6, "title").await;
+
+ test_bed.shutdown().await;
+
+ assert!(actual_item.documentation.is_some());
+ verify_text_edit(&actual_item, 1, 4, 1, 9, "title");
+}
+
+#[tokio::test]
+async fn latex_begin_command() {
+ let mut test_bed = TestBedBuilder::new().file("main.tex", r#"\"#).build().await;
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+ test_bed.open("main.tex").await;
+
+ let actual_item = run_item(&test_bed, "main.tex", 0, 1, "begin").await;
+
+ test_bed.shutdown().await;
+
+ verify_detail(&actual_item, "built-in");
+}
+
+#[cfg(feature = "citation")]
+#[tokio::test]
+async fn latex_citation_valid() {
+ let mut test_bed = TestBedBuilder::new()
+ .file(
+ "main.tex",
+ indoc!(
+ r#"
+ \documentclass{article}
+ \bibliography{main}
+ \begin{document}
+ \cite{
+ \end{document}
+ "#
+ ),
+ )
+ .file(
+ "main.bib",
+ indoc!(
+ r#"
+ @article{foo:2019,
+ author = {Foo Bar},
+ title = {Baz Qux},
+ year = {2019},
+ }
+
+ @article{bar:2005,}
+ "#
+ ),
+ )
+ .build()
+ .await;
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+ test_bed.open("main.tex").await;
+ test_bed.open("main.bib").await;
+
+ let actual_item = run_item(&test_bed, "main.tex", 3, 6, "foo:2019").await;
+
+ test_bed.shutdown().await;
+
+ verify_text_edit(&actual_item, 3, 6, 3, 6, "foo:2019");
+ assert_eq!(
+ actual_item.documentation.unwrap(),
+ Documentation::MarkupContent(MarkupContent {
+ kind: MarkupKind::Markdown,
+ value: "Bar, F. (2019). *Baz Qux*.".into()
+ })
+ );
+}
+
+#[cfg(feature = "citation")]
+#[tokio::test]
+async fn latex_citation_invalid() {
+ let mut test_bed = TestBedBuilder::new()
+ .file(
+ "main.tex",
+ indoc!(
+ r#"
+ \documentclass{article}
+ \bibliography{main}
+ \begin{document}
+ \cite{
+ \end{document}
+ "#
+ ),
+ )
+ .file(
+ "main.bib",
+ indoc!(
+ r#"
+ @article{foo:2019,
+ author = {Foo Bar},
+ title = {Baz Qux},
+ year = {2019},
+ }
+
+ @article{bar:2005,}
+ "#
+ ),
+ )
+ .build()
+ .await;
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+ test_bed.open("main.tex").await;
+ test_bed.open("main.bib").await;
+
+ let actual_item = run_item(&test_bed, "main.tex", 3, 6, "bar:2005").await;
+
+ test_bed.shutdown().await;
+
+ verify_text_edit(&actual_item, 3, 6, 3, 6, "bar:2005");
+ assert_eq!(actual_item.documentation, None);
+}
+
+#[tokio::test]
+async fn latex_color_name() {
+ let mut test_bed = TestBedBuilder::new()
+ .file(
+ "main.tex",
+ indoc!(
+ r#"
+ \color{re}
+ \definecolor{foo}{
+ \definecolorset{R}
+ "#
+ ),
+ )
+ .build()
+ .await;
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+ test_bed.open("main.tex").await;
+
+ let actual_item = run_item(&test_bed, "main.tex", 0, 9, "red").await;
+
+ test_bed.shutdown().await;
+
+ verify_text_edit(&actual_item, 0, 7, 0, 9, "red");
+}
+
+#[tokio::test]
+async fn latex_color_model_define_color() {
+ let mut test_bed = TestBedBuilder::new()
+ .file(
+ "main.tex",
+ indoc!(
+ r#"
+ \color{re}
+ \definecolor{foo}{
+ \definecolorset{R}
+ "#
+ ),
+ )
+ .build()
+ .await;
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+ test_bed.open("main.tex").await;
+
+ let actual_item = run_item(&test_bed, "main.tex", 1, 18, "rgb").await;
+
+ test_bed.shutdown().await;
+
+ verify_text_edit(&actual_item, 1, 18, 1, 18, "rgb");
+}
+
+#[tokio::test]
+async fn latex_model_define_color_set() {
+ let mut test_bed = TestBedBuilder::new()
+ .file(
+ "main.tex",
+ indoc!(
+ r#"
+ \color{re}
+ \definecolor{foo}{
+ \definecolorset{R}
+ "#
+ ),
+ )
+ .build()
+ .await;
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+ test_bed.open("main.tex").await;
+
+ let actual_item = run_item(&test_bed, "main.tex", 2, 17, "RGB").await;
+
+ test_bed.shutdown().await;
+
+ verify_text_edit(&actual_item, 2, 16, 2, 17, "RGB");
+}
+
+#[tokio::test]
+async fn latex_component_kernel_command() {
+ let mut test_bed = TestBedBuilder::new()
+ .file(
+ "main.tex",
+ indoc!(
+ r#"
+ \documentclass{book}
+ \usepackage{amsmath}
+ \chap
+ \varDel
+ \begin{theind}
+ \end{alig}
+ \begin{doc}
+ \vareps
+ "#
+ ),
+ )
+ .build()
+ .await;
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+ test_bed.open("main.tex").await;
+
+ let actual_item = run_item(&test_bed, "main.tex", 0, 1, "documentclass").await;
+
+ test_bed.shutdown().await;
+
+ verify_detail(&actual_item, "built-in");
+ verify_text_edit(&actual_item, 0, 1, 0, 14, "documentclass");
+}
+
+#[tokio::test]
+async fn latex_component_kernel_command_glyph() {
+ let mut test_bed = TestBedBuilder::new()
+ .file(
+ "main.tex",
+ indoc!(
+ r#"
+ \documentclass{book}
+ \usepackage{amsmath}
+ \chap
+ \varDel
+ \begin{theind}
+ \end{alig}
+ \begin{doc}
+ \vareps
+ "#
+ ),
+ )
+ .build()
+ .await;
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+ test_bed.open("main.tex").await;
+
+ let actual_item = run_item(&test_bed, "main.tex", 7, 7, "varepsilon").await;
+
+ test_bed.shutdown().await;
+
+ verify_detail(&actual_item, "ε, built-in");
+ verify_text_edit(&actual_item, 7, 1, 7, 7, "varepsilon");
+}
+
+#[tokio::test]
+async fn latex_component_kernel_environment() {
+ let mut test_bed = TestBedBuilder::new()
+ .file(
+ "main.tex",
+ indoc!(
+ r#"
+ \documentclass{book}
+ \usepackage{amsmath}
+ \chap
+ \varDel
+ \begin{theind}
+ \end{alig}
+ \begin{doc}
+ \vareps
+ "#
+ ),
+ )
+ .build()
+ .await;
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+ test_bed.open("main.tex").await;
+
+ let actual_item = run_item(&test_bed, "main.tex", 6, 10, "document").await;
+
+ test_bed.shutdown().await;
+
+ verify_detail(&actual_item, "built-in");
+ verify_text_edit(&actual_item, 6, 7, 6, 10, "document");
+}
+
+#[tokio::test]
+async fn latex_component_class_command() {
+ let mut test_bed = TestBedBuilder::new()
+ .file(
+ "main.tex",
+ indoc!(
+ r#"
+ \documentclass{book}
+ \usepackage{amsmath}
+ \chap
+ \varDel
+ \begin{theind}
+ \end{alig}
+ \begin{doc}
+ \vareps
+ "#
+ ),
+ )
+ .build()
+ .await;
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+ test_bed.open("main.tex").await;
+
+ let actual_item = run_item(&test_bed, "main.tex", 2, 5, "chapter").await;
+
+ test_bed.shutdown().await;
+
+ verify_detail(&actual_item, "book.cls");
+ verify_text_edit(&actual_item, 2, 1, 2, 5, "chapter");
+}
+
+#[tokio::test]
+async fn latex_component_class_environment() {
+ let mut test_bed = TestBedBuilder::new()
+ .file(
+ "main.tex",
+ indoc!(
+ r#"
+ \documentclass{book}
+ \usepackage{amsmath}
+ \chap
+ \varDel
+ \begin{theind}
+ \end{alig}
+ \begin{doc}
+ \vareps
+ "#
+ ),
+ )
+ .build()
+ .await;
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+ test_bed.open("main.tex").await;
+
+ let actual_item = run_item(&test_bed, "main.tex", 4, 13, "theindex").await;
+
+ test_bed.shutdown().await;
+
+ verify_detail(&actual_item, "book.cls");
+ verify_text_edit(&actual_item, 4, 7, 4, 13, "theindex");
+}
+
+#[tokio::test]
+async fn latex_component_package_command() {
+ let mut test_bed = TestBedBuilder::new()
+ .file(
+ "main.tex",
+ indoc!(
+ r#"
+ \documentclass{book}
+ \usepackage{amsmath}
+ \chap
+ \varDel
+ \begin{theind}
+ \end{alig}
+ \begin{doc}
+ \vareps
+ "#
+ ),
+ )
+ .build()
+ .await;
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+ test_bed.open("main.tex").await;
+
+ let actual_item = run_item(&test_bed, "main.tex", 3, 7, "varDelta").await;
+
+ test_bed.shutdown().await;
+
+ verify_detail(&actual_item, "amsmath.sty");
+ verify_text_edit(&actual_item, 3, 1, 3, 7, "varDelta");
+}
+
+#[tokio::test]
+async fn latex_component_package_environment() {
+ let mut test_bed = TestBedBuilder::new()
+ .file(
+ "main.tex",
+ indoc!(
+ r#"
+ \documentclass{book}
+ \usepackage{amsmath}
+ \chap
+ \varDel
+ \begin{theind}
+ \end{alig}
+ \begin{doc}
+ \vareps
+ "#
+ ),
+ )
+ .build()
+ .await;
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+ test_bed.open("main.tex").await;
+
+ let actual_item = run_item(&test_bed, "main.tex", 5, 5, "align").await;
+
+ test_bed.shutdown().await;
+
+ verify_detail(&actual_item, "amsmath.sty");
+ verify_text_edit(&actual_item, 5, 5, 5, 9, "align");
+}
+
+#[tokio::test]
+async fn latex_import_class() {
+ let mut test_bed = TestBedBuilder::new()
+ .file(
+ "main.tex",
+ indoc!(
+ r#"
+ \documentclass{book}
+ \usepackage{amsmath}
+ "#
+ ),
+ )
+ .build()
+ .await;
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+ test_bed.open("main.tex").await;
+
+ let actual_item = run_item(&test_bed, "main.tex", 0, 19, "book").await;
+
+ test_bed.shutdown().await;
+
+ assert!(actual_item.documentation.is_some());
+ verify_text_edit(&actual_item, 0, 15, 0, 19, "book");
+}
+
+#[tokio::test]
+async fn latex_import_package() {
+ let mut test_bed = TestBedBuilder::new()
+ .file(
+ "main.tex",
+ indoc!(
+ r#"
+ \documentclass{book}
+ \usepackage{amsmath}
+ "#
+ ),
+ )
+ .build()
+ .await;
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+ test_bed.open("main.tex").await;
+
+ let actual_item = run_item(&test_bed, "main.tex", 1, 15, "amsmath").await;
+
+ test_bed.shutdown().await;
+
+ assert!(actual_item.documentation.is_some());
+ verify_text_edit(&actual_item, 1, 12, 1, 19, "amsmath");
+}
+
+#[tokio::test]
+async fn latex_include_relative_root_no_extension() {
+ let mut test_bed = TestBedBuilder::new()
+ .file(
+ "main.tex",
+ indoc!(
+ r#"
+ \documentclass{article}
+ \include{}
+ \input{}
+ \input{qux/}
+ \addbibresource{}
+ "#
+ ),
+ )
+ .file("foo.bib", "")
+ .file("bar.tex", "")
+ .file("qux/baz.tex", "")
+ .build()
+ .await;
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+ test_bed.open("main.tex").await;
+
+ let actual_items = run_list(&test_bed, "main.tex", 1, 9).await;
+
+ test_bed.shutdown().await;
+
+ assert_eq!(actual_items, vec!["bar", "main", "qux"]);
+}
+
+#[tokio::test]
+async fn latex_include_relative_root_with_extension() {
+ let mut test_bed = TestBedBuilder::new()
+ .file(
+ "main.tex",
+ indoc!(
+ r#"
+ \documentclass{article}
+ \include{}
+ \input{}
+ \input{qux/}
+ \addbibresource{}
+ "#
+ ),
+ )
+ .file("foo.bib", "")
+ .file("bar.tex", "")
+ .file("qux/baz.tex", "")
+ .build()
+ .await;
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+ test_bed.open("main.tex").await;
+
+ let actual_items = run_list(&test_bed, "main.tex", 2, 7).await;
+
+ test_bed.shutdown().await;
+
+ assert_eq!(actual_items, vec!["bar.tex", "main.tex", "qux"]);
+}
+
+#[tokio::test]
+async fn latex_include_relative_subdir() {
+ let mut test_bed = TestBedBuilder::new()
+ .file(
+ "main.tex",
+ indoc!(
+ r#"
+ \documentclass{article}
+ \include{}
+ \input{}
+ \input{qux/}
+ \addbibresource{}
+ "#
+ ),
+ )
+ .file("foo.bib", "")
+ .file("bar.tex", "")
+ .file("qux/baz.tex", "")
+ .build()
+ .await;
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+ test_bed.open("main.tex").await;
+
+ let actual_items = run_list(&test_bed, "main.tex", 3, 11).await;
+
+ test_bed.shutdown().await;
+
+ assert_eq!(actual_items, vec!["baz.tex"]);
+}
+
+#[tokio::test]
+async fn latex_include_relative_parent_dir() {
+ let mut test_bed = TestBedBuilder::new()
+ .file(
+ "main.tex",
+ indoc!(
+ r#"
+ \documentclass{article}
+ \include{}
+ \input{}
+ \input{qux/}
+ \addbibresource{}
+ "#
+ ),
+ )
+ .file("foo.bib", "")
+ .file("bar.tex", "")
+ .file("qux/baz.tex", r#"\input{../}"#)
+ .build()
+ .await;
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+ test_bed.open("qux/baz.tex").await;
+
+ let actual_items = run_list(&test_bed, "qux/baz.tex", 0, 10).await;
+
+ test_bed.shutdown().await;
+
+ assert_eq!(actual_items, vec!["bar.tex", "main.tex", "qux"]);
+}
+
+#[tokio::test]
+async fn latex_include_relative_bibliography() {
+ let mut test_bed = TestBedBuilder::new()
+ .file(
+ "main.tex",
+ indoc!(
+ r#"
+ \documentclass{article}
+ \include{}
+ \input{}
+ \input{qux/}
+ \addbibresource{}
+ "#
+ ),
+ )
+ .file("foo.bib", "")
+ .file("bar.tex", "")
+ .file("qux/baz.tex", "")
+ .build()
+ .await;
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+ test_bed.open("main.tex").await;
+
+ let actual_items = run_list(&test_bed, "main.tex", 4, 16).await;
+
+ test_bed.shutdown().await;
+
+ assert_eq!(actual_items, vec!["foo.bib", "qux"]);
+}
+
+#[tokio::test]
+async fn latex_include_root_dir() {
+ let mut test_bed = TestBedBuilder::new()
+ .file("src/main.tex", r#"\input{}"#)
+ .root_dir(".")
+ .build()
+ .await;
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+ test_bed.open("src/main.tex").await;
+
+ let actual_items = run_list(&test_bed, "src/main.tex", 0, 7).await;
+
+ test_bed.shutdown().await;
+
+ assert_eq!(actual_items, vec!["src"]);
+}
+
+#[tokio::test]
+async fn latex_label() {
+ let mut test_bed = TestBedBuilder::new()
+ .file(
+ "foo.tex",
+ indoc!(
+ r#"
+ \documentclass{article}
+ %
+ \usepackage{amsmath}
+ \usepackage{caption}
+ \usepackage{amsthm}
+ \newtheorem{lemma}{Lemma}
+ %
+ \begin{document}
+ %
+ \section{Foo}%
+ \label{sec:foo}
+ %
+ \begin{equation}%
+ \label{eq:foo}
+ 1 + 1 = 2
+ \end{equation}
+ %
+ \begin{equation}%
+ \label{eq:bar}
+ 1 + 1 = 2
+ \end{equation}
+ %
+ \begin{figure}%
+ \LaTeX{}
+ \caption{Baz}%
+ \label{fig:baz}
+ \end{figure}
+ %
+ \begin{lemma}%
+ \label{thm:foo}
+ 1 + 1 = 2
+ \end{lemma}
+ %
+ \include{bar}
+ %
+ \end{document}
+ "#
+ ),
+ )
+ .file(
+ "foo.aux",
+ indoc!(
+ r#"
+ \relax
+ \@writefile{lof}{\contentsline {figure}{\numberline {1}{\ignorespaces Baz\relax }}{1}\protected@file@percent }
+ \providecommand*\caption@xref[2]{\@setref\relax\@undefined{#1}}
+ \newlabel{fig:baz}{{1}{1}}
+ \@writefile{toc}{\contentsline {section}{\numberline {1}Foo}{1}\protected@file@percent }
+ \newlabel{sec:foo}{{1}{1}}
+ \newlabel{eq:foo}{{1}{1}}
+ \newlabel{eq:bar}{{2}{1}}
+ \newlabel{thm:foo}{{1}{1}}
+ \@input{bar.aux}
+ "#
+ ),
+ )
+ .file(
+ "bar.tex",
+ indoc!(
+ r#"
+ \section{Bar}%
+ \label{sec:bar}
+ %
+ Lorem ipsum dolor sit amet.
+ \ref{}
+ \eqref{}
+ "#
+ ),
+ )
+ .file(
+ "bar.aux",
+ indoc!(
+ r#"
+ \relax
+ \@writefile{toc}{\contentsline {section}{\numberline {2}Bar}{2}\protected@file@percent }
+ \newlabel{sec:bar}{{2}{2}}
+ \@setckpt{bar}{
+ \setcounter{page}{3}
+ \setcounter{equation}{2}
+ \setcounter{enumi}{0}
+ \setcounter{enumii}{0}
+ \setcounter{enumiii}{0}
+ \setcounter{enumiv}{0}
+ \setcounter{footnote}{0}
+ \setcounter{mpfootnote}{0}
+ \setcounter{part}{0}
+ \setcounter{section}{2}
+ \setcounter{subsection}{0}
+ \setcounter{subsubsection}{0}
+ \setcounter{paragraph}{0}
+ \setcounter{subparagraph}{0}
+ \setcounter{figure}{1}
+ \setcounter{table}{0}
+ \setcounter{parentequation}{0}
+ \setcounter{caption@flags}{0}
+ \setcounter{ContinuedFloat}{0}
+ \setcounter{lemma}{1}
+ }
+ "#),
+ )
+ .build()
+ .await;
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+ test_bed.open("foo.tex").await;
+ test_bed.open("foo.aux").await;
+ test_bed.open("bar.tex").await;
+ test_bed.open("bar.aux").await;
+
+ let actual_items = test_bed.completion("bar.tex", 4, 5).await.unwrap();
+
+ test_bed.shutdown().await;
+
+ assert_eq!(actual_items.len(), 6);
+ verify_text_edit(&actual_items[0], 4, 5, 4, 5, "sec:bar");
+ verify_text_edit(&actual_items[1], 4, 5, 4, 5, "sec:foo");
+ verify_text_edit(&actual_items[2], 4, 5, 4, 5, "eq:foo");
+ verify_text_edit(&actual_items[3], 4, 5, 4, 5, "eq:bar");
+ verify_text_edit(&actual_items[4], 4, 5, 4, 5, "fig:baz");
+ verify_text_edit(&actual_items[5], 4, 5, 4, 5, "thm:foo");
+ verify_detail(&actual_items[0], "Section 2 (Bar)");
+ verify_detail(&actual_items[1], "Section 1 (Foo)");
+ verify_detail(&actual_items[2], "Equation (1)");
+ verify_detail(&actual_items[3], "Equation (2)");
+ verify_detail(&actual_items[4], "Figure 1");
+ verify_detail(&actual_items[5], "Lemma 1");
+ assert_eq!(
+ *actual_items[4].documentation.as_ref().unwrap(),
+ Documentation::String("Baz".into())
+ );
+}
+
+#[tokio::test]
+async fn latex_preselect_environment() {
+ let mut test_bed = TestBedBuilder::new()
+ .file(
+ "main.tex",
+ indoc!(
+ r#"
+ \begin{document}
+ \end{
+ "#
+ ),
+ )
+ .build()
+ .await;
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+ test_bed.open("main.tex").await;
+
+ let actual_item = run_item(&test_bed, "main.tex", 1, 5, "document").await;
+
+ test_bed.shutdown().await;
+
+ assert!(actual_item.preselect.unwrap());
+}
+
+#[tokio::test]
+async fn latex_theorem() {
+ let mut test_bed = TestBedBuilder::new()
+ .file(
+ "main.tex",
+ indoc!(
+ r#"
+ \documentclass{article}
+ \usepackage{amsthm}
+ \newtheorem{foo}{Foo}
+ \begin{f}
+ "#
+ ),
+ )
+ .build()
+ .await;
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+ test_bed.open("main.tex").await;
+
+ let actual_item = run_item(&test_bed, "main.tex", 3, 7, "foo").await;
+
+ test_bed.shutdown().await;
+
+ verify_text_edit(&actual_item, 3, 7, 3, 8, "foo");
+ verify_detail(&actual_item, "user-defined");
+}
+
+#[tokio::test]
+async fn latex_pgf_library() {
+ let mut test_bed = TestBedBuilder::new()
+ .file(
+ "main.tex",
+ indoc!(
+ r#"
+ \usepackage{tikz}
+ \usepgflibrary{}
+ \usetikzlibrary{}
+ "#
+ ),
+ )
+ .build()
+ .await;
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+ test_bed.open("main.tex").await;
+
+ let actual_item = run_item(&test_bed, "main.tex", 1, 15, "arrows").await;
+
+ test_bed.shutdown().await;
+
+ verify_text_edit(&actual_item, 1, 15, 1, 15, "arrows");
+}
+
+#[tokio::test]
+async fn latex_tikz_library() {
+ let mut test_bed = TestBedBuilder::new()
+ .file(
+ "main.tex",
+ indoc!(
+ r#"
+ \usepackage{tikz}
+ \usepgflibrary{}
+ \usetikzlibrary{}
+ "#
+ ),
+ )
+ .build()
+ .await;
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+ test_bed.open("main.tex").await;
+
+ let actual_item = run_item(&test_bed, "main.tex", 2, 16, "arrows").await;
+
+ test_bed.shutdown().await;
+
+ verify_text_edit(&actual_item, 2, 16, 2, 16, "arrows");
+}
+
+#[tokio::test]
+async fn latex_user_command() {
+ let mut test_bed = TestBedBuilder::new()
+ .file(
+ "main.tex",
+ indoc!(
+ r#"
+ \foo
+ \fo
+ \begin{foo}
+ \end{foo}
+ \begin{fo}
+ "#
+ ),
+ )
+ .build()
+ .await;
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+ test_bed.open("main.tex").await;
+
+ let actual_item = run_item(&test_bed, "main.tex", 1, 3, "foo").await;
+
+ test_bed.shutdown().await;
+
+ verify_detail(&actual_item, "user-defined");
+ verify_text_edit(&actual_item, 1, 1, 1, 3, "foo");
+}
+
+#[tokio::test]
+async fn latex_user_environment() {
+ let mut test_bed = TestBedBuilder::new()
+ .file(
+ "main.tex",
+ indoc!(
+ r#"
+ \foo
+ \fo
+ \begin{foo}
+ \end{foo}
+ \begin{fo}
+ "#
+ ),
+ )
+ .build()
+ .await;
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+ test_bed.open("main.tex").await;
+
+ let actual_item = run_item(&test_bed, "main.tex", 4, 7, "foo").await;
+
+ test_bed.shutdown().await;
+
+ verify_detail(&actual_item, "user-defined");
+ verify_text_edit(&actual_item, 4, 7, 4, 9, "foo");
+}
diff --git a/support/texlab/tests/integration/definition.rs b/support/texlab/tests/integration/definition.rs
new file mode 100644
index 0000000000..685b121377
--- /dev/null
+++ b/support/texlab/tests/integration/definition.rs
@@ -0,0 +1,482 @@
+use indoc::indoc;
+use texlab::{
+ protocol::{LocationLink, Range, RangeExt},
+ test::{TestBedBuilder, LOCATION_LINK_CAPABILITIES, PULL_CAPABILITIES},
+};
+
+fn verify_origin_selection_range(
+ link: &LocationLink,
+ start_line: u64,
+ start_character: u64,
+ end_line: u64,
+ end_character: u64,
+) {
+ assert_eq!(
+ link.origin_selection_range,
+ Some(Range::new_simple(
+ start_line,
+ start_character,
+ end_line,
+ end_character
+ ))
+ );
+}
+
+#[tokio::test]
+async fn empty_latex_document() {
+ let mut test_bed = TestBedBuilder::new().file("main.tex", "").build().await;
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+ test_bed.open("main.tex").await;
+
+ let actual_locations = test_bed
+ .definition_location("main.tex", 0, 0)
+ .await
+ .unwrap();
+
+ test_bed.shutdown().await;
+
+ assert!(actual_locations.is_empty());
+}
+
+#[tokio::test]
+async fn empty_bibtex_document() {
+ let mut test_bed = TestBedBuilder::new().file("main.bib", "").build().await;
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+ test_bed.open("main.bib").await;
+
+ let actual_locations = test_bed
+ .definition_location("main.bib", 0, 0)
+ .await
+ .unwrap();
+
+ test_bed.shutdown().await;
+
+ assert!(actual_locations.is_empty());
+}
+
+#[tokio::test]
+async fn bibtex_string() {
+ let mut test_bed = TestBedBuilder::new()
+ .file(
+ "main.bib",
+ indoc!(
+ r#"
+ @string{foo = "Foo"}
+ %
+ @string{bar = "Bar"}
+ %
+ @article{baz,
+ author = bar
+ }
+ "#
+ ),
+ )
+ .build()
+ .await;
+ test_bed.spawn();
+ test_bed
+ .initialize(LOCATION_LINK_CAPABILITIES.clone())
+ .await;
+ test_bed.open("main.bib").await;
+
+ let mut actual_links = test_bed.definition_link("main.bib", 5, 14).await.unwrap();
+
+ test_bed.shutdown().await;
+
+ assert_eq!(actual_links.len(), 1);
+ let link = actual_links.pop().unwrap();
+ verify_origin_selection_range(&link, 5, 13, 5, 16);
+ assert_eq!(link.target_uri, test_bed.uri("main.bib").into());
+ assert_eq!(link.target_range, Range::new_simple(2, 0, 2, 20));
+ assert_eq!(link.target_selection_range, Range::new_simple(2, 8, 2, 11));
+}
+
+#[tokio::test]
+async fn latex_citation_link() {
+ let mut test_bed = TestBedBuilder::new()
+ .file(
+ "foo.tex",
+ indoc!(
+ r#"
+ \bibliography{bar}
+ \cite{bar}
+ "#
+ ),
+ )
+ .file(
+ "bar.bib",
+ indoc!(
+ r#"
+ @article{foo,}
+ %
+ @article{bar,}
+ "#
+ ),
+ )
+ .file(
+ "baz.bib",
+ indoc!(
+ r#"
+ @article{foo,}
+ %
+ @article{bar,}
+ "#
+ ),
+ )
+ .build()
+ .await;
+ test_bed.spawn();
+ test_bed
+ .initialize(LOCATION_LINK_CAPABILITIES.clone())
+ .await;
+ test_bed.open("foo.tex").await;
+ test_bed.open("bar.bib").await;
+ test_bed.open("baz.bib").await;
+
+ let mut actual_links = test_bed.definition_link("foo.tex", 1, 7).await.unwrap();
+
+ test_bed.shutdown().await;
+
+ assert_eq!(actual_links.len(), 1);
+ let link = actual_links.pop().unwrap();
+ verify_origin_selection_range(&link, 1, 6, 1, 9);
+ assert_eq!(link.target_uri, test_bed.uri("bar.bib").into());
+ assert_eq!(link.target_range, Range::new_simple(2, 0, 2, 14));
+ assert_eq!(link.target_selection_range, Range::new_simple(2, 9, 2, 12));
+}
+
+#[tokio::test]
+async fn latex_citation_location() {
+ let mut test_bed = TestBedBuilder::new()
+ .file(
+ "foo.tex",
+ indoc!(
+ r#"
+ \bibliography{bar}
+ \cite{bar}
+ "#
+ ),
+ )
+ .file(
+ "bar.bib",
+ indoc!(
+ r#"
+ @article{foo,}
+ %
+ @article{bar,}
+ "#
+ ),
+ )
+ .file(
+ "baz.bib",
+ indoc!(
+ r#"
+ @article{foo,}
+ %
+ @article{bar,}
+ "#
+ ),
+ )
+ .build()
+ .await;
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+ test_bed.open("foo.tex").await;
+ test_bed.open("bar.bib").await;
+ test_bed.open("baz.bib").await;
+
+ let mut actual_locations = test_bed.definition_location("foo.tex", 1, 7).await.unwrap();
+
+ test_bed.shutdown().await;
+
+ assert_eq!(actual_locations.len(), 1);
+ let location = actual_locations.pop().unwrap();
+ assert_eq!(location.uri, test_bed.uri("bar.bib").into());
+ assert_eq!(location.range, Range::new_simple(2, 9, 2, 12));
+}
+
+#[tokio::test]
+async fn latex_command_link() {
+ let mut test_bed = TestBedBuilder::new()
+ .file(
+ "main.tex",
+ indoc!(
+ r#"
+ \newcommand{\foo}{Foo}
+ %
+ \foo
+ "#
+ ),
+ )
+ .build()
+ .await;
+ test_bed.spawn();
+ test_bed
+ .initialize(LOCATION_LINK_CAPABILITIES.clone())
+ .await;
+ test_bed.open("main.tex").await;
+
+ let mut actual_links = test_bed.definition_link("main.tex", 2, 2).await.unwrap();
+
+ test_bed.shutdown().await;
+
+ assert_eq!(actual_links.len(), 1);
+ let link = actual_links.pop().unwrap();
+ verify_origin_selection_range(&link, 2, 0, 2, 4);
+ assert_eq!(link.target_uri, test_bed.uri("main.tex").into());
+ assert_eq!(link.target_range, Range::new_simple(0, 0, 0, 22));
+ assert_eq!(link.target_selection_range, Range::new_simple(0, 0, 0, 22));
+}
+
+#[tokio::test]
+async fn latex_math_operator_link() {
+ let mut test_bed = TestBedBuilder::new()
+ .file(
+ "main.tex",
+ indoc!(
+ r#"
+ \DeclareMathOperator{\foo}{foo}
+ %
+ \foo
+ "#
+ ),
+ )
+ .build()
+ .await;
+ test_bed.spawn();
+ test_bed
+ .initialize(LOCATION_LINK_CAPABILITIES.clone())
+ .await;
+ test_bed.open("main.tex").await;
+
+ let mut actual_links = test_bed.definition_link("main.tex", 2, 2).await.unwrap();
+
+ test_bed.shutdown().await;
+
+ assert_eq!(actual_links.len(), 1);
+ let link = actual_links.pop().unwrap();
+ verify_origin_selection_range(&link, 2, 0, 2, 4);
+ assert_eq!(link.target_uri, test_bed.uri("main.tex").into());
+ assert_eq!(link.target_range, Range::new_simple(0, 0, 0, 31));
+ assert_eq!(link.target_selection_range, Range::new_simple(0, 0, 0, 31));
+}
+
+#[tokio::test]
+async fn latex_label_unknown_link() {
+ let mut test_bed = TestBedBuilder::new()
+ .file(
+ "main.tex",
+ indoc!(
+ r#"
+ \label{foo}
+ \ref{foo}
+ "#
+ ),
+ )
+ .build()
+ .await;
+ test_bed.spawn();
+ test_bed
+ .initialize(LOCATION_LINK_CAPABILITIES.clone())
+ .await;
+ test_bed.open("main.tex").await;
+
+ let mut actual_links = test_bed.definition_link("main.tex", 1, 7).await.unwrap();
+
+ test_bed.shutdown().await;
+
+ assert_eq!(actual_links.len(), 1);
+ let link = actual_links.pop().unwrap();
+ verify_origin_selection_range(&link, 1, 5, 1, 8);
+ assert_eq!(link.target_uri, test_bed.uri("main.tex").into());
+ assert_eq!(link.target_range, Range::new_simple(0, 0, 0, 11));
+ assert_eq!(link.target_selection_range, Range::new_simple(0, 0, 0, 11));
+}
+
+#[tokio::test]
+async fn latex_label_equation_link() {
+ let mut test_bed = TestBedBuilder::new()
+ .file(
+ "main.tex",
+ indoc!(
+ r#"
+ \begin{equation}%
+ \label{eq:foo}
+ Foo
+ \end{equation}
+ %
+ \ref{eq:foo}
+ "#
+ ),
+ )
+ .build()
+ .await;
+ test_bed.spawn();
+ test_bed
+ .initialize(LOCATION_LINK_CAPABILITIES.clone())
+ .await;
+ test_bed.open("main.tex").await;
+
+ let mut actual_links = test_bed.definition_link("main.tex", 5, 8).await.unwrap();
+
+ test_bed.shutdown().await;
+
+ assert_eq!(actual_links.len(), 1);
+ let link = actual_links.pop().unwrap();
+ verify_origin_selection_range(&link, 5, 5, 5, 11);
+ assert_eq!(link.target_uri, test_bed.uri("main.tex").into());
+ assert_eq!(link.target_range, Range::new_simple(0, 0, 3, 14));
+ assert_eq!(link.target_selection_range, Range::new_simple(1, 0, 1, 14));
+}
+
+#[tokio::test]
+async fn latex_label_float_link() {
+ let mut test_bed = TestBedBuilder::new()
+ .file(
+ "main.tex",
+ indoc!(
+ r#"
+ \begin{figure}
+ Foo
+ \caption{Bar}
+ \label{fig}
+ \end{figure}
+ %
+ \ref{fig}
+ "#
+ ),
+ )
+ .build()
+ .await;
+ test_bed.spawn();
+ test_bed
+ .initialize(LOCATION_LINK_CAPABILITIES.clone())
+ .await;
+ test_bed.open("main.tex").await;
+
+ let mut actual_links = test_bed.definition_link("main.tex", 6, 6).await.unwrap();
+
+ test_bed.shutdown().await;
+
+ assert_eq!(actual_links.len(), 1);
+ let link = actual_links.pop().unwrap();
+ verify_origin_selection_range(&link, 6, 5, 6, 8);
+ assert_eq!(link.target_uri, test_bed.uri("main.tex").into());
+ assert_eq!(link.target_range, Range::new_simple(0, 0, 4, 12));
+ assert_eq!(link.target_selection_range, Range::new_simple(3, 0, 3, 11));
+}
+
+#[tokio::test]
+async fn latex_label_item_link() {
+ let mut test_bed = TestBedBuilder::new()
+ .file(
+ "main.tex",
+ indoc!(
+ r#"
+ \begin{enumerate}
+ \item Foo
+ \item\label{bar} Bar
+ \item Baz
+ \end{enumerate}
+ %
+ \ref{bar}
+ "#
+ ),
+ )
+ .build()
+ .await;
+ test_bed.spawn();
+ test_bed
+ .initialize(LOCATION_LINK_CAPABILITIES.clone())
+ .await;
+ test_bed.open("main.tex").await;
+
+ let mut actual_links = test_bed.definition_link("main.tex", 6, 6).await.unwrap();
+
+ test_bed.shutdown().await;
+
+ assert_eq!(actual_links.len(), 1);
+ let link = actual_links.pop().unwrap();
+ verify_origin_selection_range(&link, 6, 5, 6, 8);
+ assert_eq!(link.target_uri, test_bed.uri("main.tex").into());
+ assert_eq!(link.target_range, Range::new_simple(0, 0, 4, 15));
+ assert_eq!(link.target_selection_range, Range::new_simple(2, 9, 2, 20));
+}
+
+#[tokio::test]
+async fn latex_label_section_link() {
+ let mut test_bed = TestBedBuilder::new()
+ .file(
+ "main.tex",
+ indoc!(
+ r#"
+ \section{Foo}
+ \label{sec:foo}
+ %
+ \section{Bar}
+ \label{sec:bar}
+ %
+ \ref{sec:foo}
+ "#
+ ),
+ )
+ .build()
+ .await;
+ test_bed.spawn();
+ test_bed
+ .initialize(LOCATION_LINK_CAPABILITIES.clone())
+ .await;
+ test_bed.open("main.tex").await;
+
+ let mut actual_links = test_bed.definition_link("main.tex", 6, 6).await.unwrap();
+
+ test_bed.shutdown().await;
+
+ assert_eq!(actual_links.len(), 1);
+ let link = actual_links.pop().unwrap();
+ verify_origin_selection_range(&link, 6, 5, 6, 12);
+ assert_eq!(link.target_uri, test_bed.uri("main.tex").into());
+ assert_eq!(link.target_range, Range::new_simple(0, 0, 3, 0));
+ assert_eq!(link.target_selection_range, Range::new_simple(1, 0, 1, 15));
+}
+
+#[tokio::test]
+async fn latex_label_theorem_link() {
+ let mut test_bed = TestBedBuilder::new()
+ .file(
+ "main.tex",
+ indoc!(
+ r#"
+ \usepackage{amsthm}
+ \newtheorem{lemma}{Lemma}
+ %
+ \begin{lemma}%
+ \label{thm:foo}
+ Foo
+ \end{lemma}
+ %
+ \ref{thm:foo}
+ "#
+ ),
+ )
+ .build()
+ .await;
+ test_bed.spawn();
+ test_bed
+ .initialize(LOCATION_LINK_CAPABILITIES.clone())
+ .await;
+ test_bed.open("main.tex").await;
+
+ let mut actual_links = test_bed.definition_link("main.tex", 8, 7).await.unwrap();
+
+ test_bed.shutdown().await;
+
+ assert_eq!(actual_links.len(), 1);
+ let link = actual_links.pop().unwrap();
+ verify_origin_selection_range(&link, 8, 5, 8, 12);
+ assert_eq!(link.target_uri, test_bed.uri("main.tex").into());
+ assert_eq!(link.target_range, Range::new_simple(3, 0, 6, 11));
+ assert_eq!(link.target_selection_range, Range::new_simple(4, 0, 4, 15));
+}
diff --git a/support/texlab/tests/integration/document_symbol.rs b/support/texlab/tests/integration/document_symbol.rs
new file mode 100644
index 0000000000..afc3d54831
--- /dev/null
+++ b/support/texlab/tests/integration/document_symbol.rs
@@ -0,0 +1,424 @@
+use indoc::indoc;
+use texlab::{
+ protocol::{DocumentSymbol, Range, RangeExt},
+ test::{TestBedBuilder, NESTED_SYMBOL_CAPABILITIES},
+};
+
+fn verify_symbol(
+ symbol: &DocumentSymbol,
+ name: &str,
+ detail: Option<&str>,
+ selection_range: Range,
+ range: Range,
+) {
+ assert_eq!(symbol.name, name);
+ assert_eq!(symbol.detail.as_ref().map(AsRef::as_ref), detail);
+ assert_eq!(symbol.selection_range, selection_range);
+ assert_eq!(symbol.range, range);
+}
+
+#[tokio::test]
+async fn enumerate() {
+ let mut test_bed = TestBedBuilder::new()
+ .file(
+ "main.tex",
+ indoc!(
+ r#"
+ \documentclass{article}
+ %
+ \begin{document}
+ %
+ \begin{enumerate}
+ \item\label{it:foo} Foo
+ \item\label{it:bar} Bar
+ \item[Baz] Baz
+ \item[Qux]\label{it:qux} Qux
+ \end{enumerate}
+ %
+ \end{document}
+ "#
+ ),
+ )
+ .file(
+ "main.aux",
+ indoc!(
+ r#"
+ \relax
+ \newlabel{it:foo}{{1}{1}}
+ \newlabel{it:qux}{{2}{1}}
+ "#
+ ),
+ )
+ .build()
+ .await;
+
+ test_bed.spawn();
+ test_bed
+ .initialize(NESTED_SYMBOL_CAPABILITIES.clone())
+ .await;
+ test_bed.open("main.tex").await;
+ test_bed.open("main.aux").await;
+
+ let mut actual_symbols = test_bed.document_symbol_nested("main.tex").await.unwrap();
+
+ test_bed.shutdown().await;
+
+ assert_eq!(actual_symbols.len(), 1);
+ verify_symbol(
+ &actual_symbols[0],
+ "Enumerate",
+ None,
+ Range::new_simple(4, 0, 9, 15),
+ Range::new_simple(4, 0, 9, 15),
+ );
+
+ let children = actual_symbols[0].children.take().unwrap();
+ assert_eq!(children.len(), 4);
+ verify_symbol(
+ &children[0],
+ "1",
+ Some("it:foo"),
+ Range::new_simple(5, 9, 5, 23),
+ Range::new_simple(5, 4, 6, 4),
+ );
+ verify_symbol(
+ &children[1],
+ "Item",
+ Some("it:bar"),
+ Range::new_simple(6, 9, 6, 23),
+ Range::new_simple(6, 4, 7, 4),
+ );
+ verify_symbol(
+ &children[2],
+ "Baz",
+ None,
+ Range::new_simple(7, 4, 7, 14),
+ Range::new_simple(7, 4, 8, 4),
+ );
+ verify_symbol(
+ &children[3],
+ "Qux",
+ Some("it:qux"),
+ Range::new_simple(8, 14, 8, 28),
+ Range::new_simple(8, 4, 9, 0),
+ );
+}
+
+#[tokio::test]
+async fn equation() {
+ let mut test_bed = TestBedBuilder::new()
+ .file(
+ "main.tex",
+ indoc!(
+ r#"
+ \documentclass{article}
+ %
+ \begin{document}
+ %
+ \begin{equation}\label{eq:foo}
+ Foo
+ \end{equation}
+ %
+ \begin{equation}\label{eq:bar}
+ Bar
+ \end{equation}
+ %
+ \begin{equation}
+ Baz
+ \end{equation}
+ %
+ \end{document}
+ "#
+ ),
+ )
+ .file(
+ "main.aux",
+ indoc!(
+ r#"
+ \relax
+ \newlabel{eq:foo}{{1}{1}}
+ "#
+ ),
+ )
+ .build()
+ .await;
+
+ test_bed.spawn();
+ test_bed
+ .initialize(NESTED_SYMBOL_CAPABILITIES.clone())
+ .await;
+ test_bed.open("main.tex").await;
+ test_bed.open("main.aux").await;
+
+ let actual_symbols = test_bed.document_symbol_nested("main.tex").await.unwrap();
+
+ test_bed.shutdown().await;
+
+ assert_eq!(actual_symbols.len(), 3);
+ verify_symbol(
+ &actual_symbols[0],
+ "Equation (1)",
+ Some("eq:foo"),
+ Range::new_simple(4, 16, 4, 30),
+ Range::new_simple(4, 0, 6, 14),
+ );
+ verify_symbol(
+ &actual_symbols[1],
+ "Equation",
+ Some("eq:bar"),
+ Range::new_simple(8, 16, 8, 30),
+ Range::new_simple(8, 0, 10, 14),
+ );
+ verify_symbol(
+ &actual_symbols[2],
+ "Equation",
+ None,
+ Range::new_simple(12, 0, 14, 14),
+ Range::new_simple(12, 0, 14, 14),
+ );
+}
+
+#[tokio::test]
+async fn float() {
+ let mut test_bed = TestBedBuilder::new()
+ .file(
+ "main.tex",
+ indoc!(
+ r#"
+ \documentclass{article}
+ %
+ \begin{document}
+ %
+ \begin{figure}
+ Foo
+ \caption{Foo}\label{fig:foo}
+ \end{figure}
+ %
+ \begin{figure}
+ Bar
+ \caption{Bar}\label{fig:bar}
+ \end{figure}
+ %
+ \begin{figure}
+ Baz
+ \caption{Baz}
+ \end{figure}
+ %
+ \begin{figure}
+ Qux
+ \end{figure}
+ %
+ \end{document}
+ "#
+ ),
+ )
+ .file(
+ "main.aux",
+ indoc!(
+ r#"
+ \relax
+ \@writefile{lof}{\contentsline {figure}{\numberline {1}{\ignorespaces Foo}}{1}\protected@file@percent }
+ \newlabel{fig:foo}{{1}{1}}
+ \@writefile{lof}{\contentsline {figure}{\numberline {2}{\ignorespaces Bar}}{1}\protected@file@percent }
+ \@writefile{lof}{\contentsline {figure}{\numberline {3}{\ignorespaces Baz}}{1}\protected@file@percent }
+ "#
+ ),
+ )
+ .build()
+ .await;
+
+ test_bed.spawn();
+ test_bed
+ .initialize(NESTED_SYMBOL_CAPABILITIES.clone())
+ .await;
+ test_bed.open("main.tex").await;
+ test_bed.open("main.aux").await;
+
+ let actual_symbols = test_bed.document_symbol_nested("main.tex").await.unwrap();
+
+ test_bed.shutdown().await;
+
+ assert_eq!(actual_symbols.len(), 3);
+ verify_symbol(
+ &actual_symbols[0],
+ "Figure 1: Foo",
+ Some("fig:foo"),
+ Range::new_simple(6, 17, 6, 32),
+ Range::new_simple(4, 0, 7, 12),
+ );
+ verify_symbol(
+ &actual_symbols[1],
+ "Figure: Bar",
+ Some("fig:bar"),
+ Range::new_simple(11, 17, 11, 32),
+ Range::new_simple(9, 0, 12, 12),
+ );
+ verify_symbol(
+ &actual_symbols[2],
+ "Figure: Baz",
+ None,
+ Range::new_simple(14, 0, 17, 12),
+ Range::new_simple(14, 0, 17, 12),
+ );
+}
+
+#[tokio::test]
+async fn section() {
+ let mut test_bed = TestBedBuilder::new()
+ .file(
+ "main.tex",
+ indoc!(
+ r#"
+ \documentclass{article}
+ %
+ \begin{document}
+ %
+ \section{Foo}
+ %
+ \section{Bar}\label{sec:bar}
+ %
+ \subsection{Baz}\label{sec:baz}
+ %
+ \end{document}
+ "#
+ ),
+ )
+ .file(
+ "main.aux",
+ indoc!(
+ r#"
+ \relax
+ \@writefile{toc}{\contentsline {section}{\numberline {1}Foo}{1}\protected@file@percent }
+ \@writefile{toc}{\contentsline {section}{\numberline {2}Bar}{1}\protected@file@percent }
+ \newlabel{sec:bar}{{2}{1}}
+ "#
+ ),
+ )
+ .build()
+ .await;
+
+ test_bed.spawn();
+ test_bed
+ .initialize(NESTED_SYMBOL_CAPABILITIES.clone())
+ .await;
+ test_bed.open("main.tex").await;
+ test_bed.open("main.aux").await;
+
+ let mut actual_symbols = test_bed.document_symbol_nested("main.tex").await.unwrap();
+
+ test_bed.shutdown().await;
+
+ assert_eq!(actual_symbols.len(), 2);
+ verify_symbol(
+ &actual_symbols[0],
+ "Foo",
+ None,
+ Range::new_simple(4, 0, 4, 13),
+ Range::new_simple(4, 0, 6, 0),
+ );
+ verify_symbol(
+ &actual_symbols[1],
+ "2 Bar",
+ Some("sec:bar"),
+ Range::new_simple(6, 0, 6, 13),
+ Range::new_simple(6, 0, 10, 0),
+ );
+
+ let children = actual_symbols[1].children.take().unwrap();
+ assert_eq!(children.len(), 1);
+ verify_symbol(
+ &children[0],
+ "Baz",
+ Some("sec:baz"),
+ Range::new_simple(8, 0, 8, 16),
+ Range::new_simple(8, 0, 10, 0),
+ );
+}
+
+#[tokio::test]
+async fn theorem() {
+ let mut test_bed = TestBedBuilder::new()
+ .file(
+ "main.tex",
+ indoc!(
+ r#"
+ \documentclass{article}
+ \usepackage{amsthm}
+ \newtheorem{lemma}{Lemma}
+ %
+ \begin{document}
+ %
+ \begin{lemma}[Foo]\label{thm:foo}
+ Foo
+ \end{lemma}
+ %
+ \begin{lemma}\label{thm:bar}
+ Bar
+ \end{lemma}
+ %
+ \begin{lemma}\label{thm:baz}
+ Baz
+ \end{lemma}
+ %
+ \begin{lemma}[Qux]
+ Qux
+ \end{lemma}
+ %
+ \end{document}
+ "#
+ ),
+ )
+ .file(
+ "main.aux",
+ indoc!(
+ r#"
+ \relax
+ \newlabel{thm:foo}{{1}{1}}
+ \newlabel{thm:bar}{{2}{1}}
+ "#
+ ),
+ )
+ .build()
+ .await;
+
+ test_bed.spawn();
+ test_bed
+ .initialize(NESTED_SYMBOL_CAPABILITIES.clone())
+ .await;
+ test_bed.open("main.tex").await;
+ test_bed.open("main.aux").await;
+
+ let actual_symbols = test_bed.document_symbol_nested("main.tex").await.unwrap();
+
+ test_bed.shutdown().await;
+
+ assert_eq!(actual_symbols.len(), 4);
+ verify_symbol(
+ &actual_symbols[0],
+ "Lemma 1 (Foo)",
+ Some("thm:foo"),
+ Range::new_simple(6, 18, 6, 33),
+ Range::new_simple(6, 0, 8, 11),
+ );
+ verify_symbol(
+ &actual_symbols[1],
+ "Lemma 2",
+ Some("thm:bar"),
+ Range::new_simple(10, 13, 10, 28),
+ Range::new_simple(10, 0, 12, 11),
+ );
+ verify_symbol(
+ &actual_symbols[2],
+ "Lemma",
+ Some("thm:baz"),
+ Range::new_simple(14, 13, 14, 28),
+ Range::new_simple(14, 0, 16, 11),
+ );
+ verify_symbol(
+ &actual_symbols[3],
+ "Lemma (Qux)",
+ None,
+ Range::new_simple(18, 0, 20, 11),
+ Range::new_simple(18, 0, 20, 11),
+ );
+}
diff --git a/support/texlab/tests/integration/folding.rs b/support/texlab/tests/integration/folding.rs
new file mode 100644
index 0000000000..3540e2c680
--- /dev/null
+++ b/support/texlab/tests/integration/folding.rs
@@ -0,0 +1,126 @@
+use indoc::indoc;
+use texlab::{
+ protocol::{FoldingRange, FoldingRangeKind},
+ test::{TestBedBuilder, PULL_CAPABILITIES},
+};
+
+#[tokio::test]
+async fn empty_latex_document() {
+ let mut test_bed = TestBedBuilder::new().file("main.tex", "").build().await;
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+ test_bed.open("main.tex").await;
+
+ let actual_foldings = test_bed.folding_range("main.tex").await.unwrap();
+
+ test_bed.shutdown().await;
+
+ assert!(actual_foldings.is_empty());
+}
+
+#[tokio::test]
+async fn empty_bibtex_document() {
+ let mut test_bed = TestBedBuilder::new().file("main.bib", "").build().await;
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+ test_bed.open("main.bib").await;
+
+ let actual_foldings = test_bed.folding_range("main.bib").await.unwrap();
+
+ test_bed.shutdown().await;
+
+ assert!(actual_foldings.is_empty());
+}
+
+#[tokio::test]
+async fn latex_sections_with_env() {
+ let mut test_bed = TestBedBuilder::new()
+ .file(
+ "main.tex",
+ indoc!(
+ r#"
+ \begin{document}
+ \section{Foo}
+ Foo
+ \section{Bar}
+ Bar
+ \end{document}
+ "#
+ ),
+ )
+ .build()
+ .await;
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+ test_bed.open("main.tex").await;
+
+ let actual_foldings = test_bed.folding_range("main.tex").await.unwrap();
+
+ test_bed.shutdown().await;
+
+ let expected_foldings = vec![
+ FoldingRange {
+ start_line: 0,
+ start_character: Some(16),
+ end_line: 5,
+ end_character: Some(0),
+ kind: Some(FoldingRangeKind::Region),
+ },
+ FoldingRange {
+ start_line: 1,
+ start_character: Some(13),
+ end_line: 2,
+ end_character: Some(0),
+ kind: Some(FoldingRangeKind::Region),
+ },
+ ];
+ assert_eq!(actual_foldings, expected_foldings);
+}
+
+#[tokio::test]
+async fn bibtex_entry() {
+ let mut test_bed = TestBedBuilder::new()
+ .file(
+ "main.bib",
+ indoc!(
+ r#"
+ @article{foo,
+ author = {Foo Bar},
+ title = {Baz Qux},
+ }
+ "#
+ ),
+ )
+ .build()
+ .await;
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+ test_bed.open("main.bib").await;
+
+ let actual_foldings = test_bed.folding_range("main.bib").await.unwrap();
+
+ test_bed.shutdown().await;
+
+ let expected_foldings = vec![FoldingRange {
+ start_line: 0,
+ start_character: Some(0),
+ end_line: 3,
+ end_character: Some(1),
+ kind: Some(FoldingRangeKind::Region),
+ }];
+ assert_eq!(actual_foldings, expected_foldings);
+}
+
+#[tokio::test]
+async fn unknown_file() {
+ let mut test_bed = TestBedBuilder::new().build().await;
+
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+
+ let actual_foldings = test_bed.folding_range("main.tex").await;
+
+ test_bed.shutdown().await;
+
+ assert_eq!(actual_foldings, None);
+}
diff --git a/support/texlab/tests/integration/highlight.rs b/support/texlab/tests/integration/highlight.rs
new file mode 100644
index 0000000000..ba77a7b8e3
--- /dev/null
+++ b/support/texlab/tests/integration/highlight.rs
@@ -0,0 +1,83 @@
+use indoc::indoc;
+use texlab::{
+ protocol::{DocumentHighlight, DocumentHighlightKind, Range, RangeExt},
+ test::{TestBedBuilder, PULL_CAPABILITIES},
+};
+
+#[tokio::test]
+async fn empty_latex_document() {
+ let mut test_bed = TestBedBuilder::new().file("main.tex", "").build().await;
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+ test_bed.open("main.tex").await;
+
+ let actual_highlights = test_bed.document_highlight("main.tex", 0, 0).await.unwrap();
+
+ test_bed.shutdown().await;
+
+ assert!(actual_highlights.is_empty());
+}
+
+#[tokio::test]
+async fn empty_bibtex_document() {
+ let mut test_bed = TestBedBuilder::new().file("main.bib", "").build().await;
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+ test_bed.open("main.bib").await;
+
+ let actual_highlights = test_bed.document_highlight("main.bib", 0, 0).await.unwrap();
+
+ test_bed.shutdown().await;
+
+ assert!(actual_highlights.is_empty());
+}
+
+#[tokio::test]
+async fn label() {
+ let mut test_bed = TestBedBuilder::new()
+ .file(
+ "main.tex",
+ indoc!(
+ r#"
+ \label{foo}
+ \ref{foo}
+ "#
+ ),
+ )
+ .build()
+ .await;
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+ test_bed.open("main.tex").await;
+
+ let actual_highlights = test_bed.document_highlight("main.tex", 0, 7).await.unwrap();
+
+ let expected_highlights = vec![
+ DocumentHighlight {
+ range: Range::new_simple(0, 7, 0, 10),
+ kind: Some(DocumentHighlightKind::Write),
+ },
+ DocumentHighlight {
+ range: Range::new_simple(1, 5, 1, 8),
+ kind: Some(DocumentHighlightKind::Read),
+ },
+ ];
+
+ test_bed.shutdown().await;
+
+ assert_eq!(actual_highlights, expected_highlights);
+}
+
+#[tokio::test]
+async fn unknown_file() {
+ let mut test_bed = TestBedBuilder::new().build().await;
+
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+
+ let actual_highlights = test_bed.document_highlight("main.tex", 0, 0).await;
+
+ test_bed.shutdown().await;
+
+ assert_eq!(actual_highlights, None);
+}
diff --git a/support/texlab/tests/integration/hover.rs b/support/texlab/tests/integration/hover.rs
new file mode 100644
index 0000000000..319ebcc5e9
--- /dev/null
+++ b/support/texlab/tests/integration/hover.rs
@@ -0,0 +1,86 @@
+use indoc::indoc;
+use texlab::{
+ protocol::{HoverContents, MarkupContent, MarkupKind},
+ test::{TestBedBuilder, PULL_CAPABILITIES},
+};
+
+#[tokio::test]
+async fn label_theorem_child_file() {
+ let mut test_bed = TestBedBuilder::new()
+ .file(
+ "main.tex",
+ indoc!(
+ r#"
+ \documentclass{article}
+ \newtheorem{lemma}{Lemma}
+ \include{child}
+ \ref{thm:foo}
+ "#
+ ),
+ )
+ .file(
+ "child.tex",
+ indoc!(
+ r#"
+ \begin{lemma}\label{thm:foo}
+ 1 + 1 = 2
+ \end{lemma}
+ "#
+ ),
+ )
+ .build()
+ .await;
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+ test_bed.open("main.tex").await;
+
+ let actual_hover = test_bed.hover("main.tex", 3, 8).await.unwrap().unwrap();
+ assert_eq!(
+ actual_hover.contents,
+ HoverContents::Markup(MarkupContent {
+ kind: MarkupKind::PlainText,
+ value: "Lemma".into()
+ })
+ );
+}
+
+#[tokio::test]
+async fn label_theorem_child_file_number() {
+ let mut test_bed = TestBedBuilder::new()
+ .file(
+ "main.tex",
+ indoc!(
+ r#"
+ \documentclass{article}
+ \newtheorem{lemma}{Lemma}
+ \include{child}
+ \ref{thm:foo}
+ "#
+ ),
+ )
+ .file(
+ "child.tex",
+ indoc!(
+ r#"
+ \begin{lemma}[Foo]\label{thm:foo}
+ 1 + 1 = 2
+ \end{lemma}
+ "#
+ ),
+ )
+ .file("child.aux", r#"\newlabel{thm:foo}{{1}{1}{Foo}{lemma.1}{}}"#)
+ .build()
+ .await;
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+ test_bed.open("main.tex").await;
+
+ let actual_hover = test_bed.hover("main.tex", 3, 8).await.unwrap().unwrap();
+ assert_eq!(
+ actual_hover.contents,
+ HoverContents::Markup(MarkupContent {
+ kind: MarkupKind::PlainText,
+ value: "Lemma 1 (Foo)".into()
+ })
+ );
+}
diff --git a/support/texlab/tests/integration/issues.rs b/support/texlab/tests/integration/issues.rs
new file mode 100644
index 0000000000..59a1bfeacb
--- /dev/null
+++ b/support/texlab/tests/integration/issues.rs
@@ -0,0 +1,350 @@
+use indoc::indoc;
+use texlab::{
+ protocol::{HoverContents, Location, LocationLink, MarkupContent, MarkupKind, Range, RangeExt},
+ test::{TestBedBuilder, LOCATION_LINK_CAPABILITIES, PULL_CAPABILITIES},
+};
+
+#[tokio::test]
+async fn issue_14() {
+ let mut test_bed = TestBedBuilder::new()
+ .file("main.tex", r#"\(\be\)"#)
+ .build()
+ .await;
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+ test_bed.open("main.tex").await;
+
+ let actual_items = test_bed.completion("main.tex", 0, 5).await.unwrap();
+
+ test_bed.shutdown().await;
+ assert!(actual_items.iter().any(|item| item.label == "beta"));
+}
+
+#[tokio::test]
+async fn issue_15_link() {
+ let mut test_bed = TestBedBuilder::new()
+ .file(
+ "main.tex",
+ indoc!(
+ r#"
+ \documentclass{article}
+ \begin{document}
+ \newcommand{\test}{test}
+ hello \test{}
+ \end{document}
+ "#
+ ),
+ )
+ .build()
+ .await;
+ test_bed.spawn();
+ test_bed
+ .initialize(LOCATION_LINK_CAPABILITIES.clone())
+ .await;
+ test_bed.open("main.tex").await;
+
+ let actual_links = test_bed.definition_link("main.tex", 3, 9).await.unwrap();
+
+ test_bed.shutdown().await;
+ let expected_links = vec![LocationLink {
+ origin_selection_range: Some(Range::new_simple(3, 6, 3, 13)),
+ target_range: Range::new_simple(2, 0, 2, 24),
+ target_selection_range: Range::new_simple(2, 0, 2, 24),
+ target_uri: test_bed.uri("main.tex").into(),
+ }];
+ assert_eq!(actual_links, expected_links);
+}
+
+#[tokio::test]
+async fn issue_15_location() {
+ let mut test_bed = TestBedBuilder::new()
+ .file(
+ "main.tex",
+ indoc!(
+ r#"
+ \documentclass{article}
+ \begin{document}
+ \newcommand{\test}{test}
+ hello \test{}
+ \end{document}
+ "#
+ ),
+ )
+ .build()
+ .await;
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+ test_bed.open("main.tex").await;
+
+ let actual_locations = test_bed
+ .definition_location("main.tex", 3, 9)
+ .await
+ .unwrap();
+
+ test_bed.shutdown().await;
+ let expected_locations = vec![Location {
+ range: Range::new_simple(2, 0, 2, 24),
+ uri: test_bed.uri("main.tex").into(),
+ }];
+ assert_eq!(actual_locations, expected_locations);
+}
+
+#[tokio::test]
+async fn issue_17() {
+ let mut test_bed = TestBedBuilder::new()
+ .file("main.bib", r#"@ARTICLE{foo,}"#)
+ .build()
+ .await;
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+ test_bed.open("main.bib").await;
+
+ let actual_hover = test_bed.hover("main.bib", 0, 3).await.unwrap().unwrap();
+
+ test_bed.shutdown().await;
+ assert_eq!(actual_hover.range.unwrap(), Range::new_simple(0, 0, 0, 8));
+}
+
+#[tokio::test]
+async fn issue_21() {
+ let mut test_bed = TestBedBuilder::new()
+ .file(
+ "main.tex",
+ indoc!(
+ r#"
+ \label{foo}
+ \cref{}
+ "#
+ ),
+ )
+ .build()
+ .await;
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+ test_bed.open("main.tex").await;
+
+ let actual_items = test_bed.completion("main.tex", 1, 6).await.unwrap();
+
+ test_bed.shutdown().await;
+ assert!(actual_items.iter().any(|item| item.label == "foo"));
+}
+
+#[tokio::test]
+async fn issue_22_include() {
+ let mut test_bed = TestBedBuilder::new()
+ .file(
+ "main.tex",
+ indoc!(
+ r#"
+ \bibliography{bibfile}
+ \cite{}
+ "#
+ ),
+ )
+ .file("bibfile.bib", r#"@article{foo,}"#)
+ .build()
+ .await;
+
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+ test_bed.open("main.tex").await;
+
+ let actual_items = test_bed.completion("main.tex", 1, 6).await.unwrap();
+
+ test_bed.shutdown().await;
+ assert!(actual_items.iter().any(|item| item.label == "foo"));
+}
+
+#[tokio::test]
+async fn issue_22_definition() {
+ let mut test_bed = TestBedBuilder::new()
+ .file(
+ "main.tex",
+ indoc!(
+ r#"
+ \bibliography{bibfile}
+ \cite{A,B}
+ "#
+ ),
+ )
+ .file(
+ "bibfile.bib",
+ indoc!(
+ r#"
+ @article{A,}
+ @article{B,}
+ "#
+ ),
+ )
+ .build()
+ .await;
+
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+ test_bed.open("main.tex").await;
+
+ let actual_locations = test_bed
+ .definition_location("main.tex", 1, 8)
+ .await
+ .unwrap();
+
+ test_bed.shutdown().await;
+ let expected_locations = vec![Location {
+ range: Range::new_simple(1, 9, 1, 10),
+ uri: test_bed.uri("bibfile.bib").into(),
+ }];
+ assert_eq!(actual_locations, expected_locations);
+}
+
+#[tokio::test]
+async fn issue_23_completion() {
+ let mut test_bed = TestBedBuilder::new()
+ .file(
+ "main.tex",
+ indoc!(
+ r#"
+ \documentclass{article}
+ \begin{document}
+ \include{test1}
+ \include{test2}
+ \end{document}
+ "#
+ ),
+ )
+ .file("test1.tex", r#"\section{Section 1}\label{sec:1}"#)
+ .file(
+ "test2.tex",
+ indoc!(
+ r#"
+ \section{Section 2}\label{sec:2}
+ %
+ This section continues from Section \ref{sec}"#
+ ),
+ )
+ .build()
+ .await;
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+ test_bed.open("test2.tex").await;
+ test_bed.detect_root("test2.tex").await;
+
+ let actual_items = test_bed.completion("test2.tex", 2, 42).await.unwrap();
+
+ test_bed.shutdown().await;
+ assert_eq!(actual_items.len(), 2);
+}
+
+#[tokio::test]
+async fn issue_23_rename() {
+ let mut test_bed = TestBedBuilder::new()
+ .file(
+ "main.tex",
+ indoc!(
+ r#"
+ \documentclass{article}
+ \begin{document}
+ \include{test1}
+ \include{test2}
+ \end{document}
+ "#
+ ),
+ )
+ .file("test1.tex", r#"\section{Section 1}\label{sec:1}"#)
+ .file(
+ "test2.tex",
+ indoc!(
+ r#"
+ \section{Section 2}\label{sec:2}
+ %
+ This section continues from Section \ref{sec:1}"#
+ ),
+ )
+ .build()
+ .await;
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+ test_bed.open("test1.tex").await;
+ test_bed.detect_root("test1.tex").await;
+
+ let workspace_edit = test_bed
+ .rename("test1.tex", 0, 27, "foo")
+ .await
+ .unwrap()
+ .unwrap();
+
+ test_bed.shutdown().await;
+ let changes = workspace_edit.changes.unwrap();
+ assert!(changes.contains_key(&test_bed.uri("test1.tex")));
+ assert!(changes.contains_key(&test_bed.uri("test2.tex")));
+}
+
+#[tokio::test]
+async fn issue_23_hover() {
+ let mut test_bed = TestBedBuilder::new()
+ .file(
+ "main.tex",
+ indoc!(
+ r#"
+ \documentclass{article}
+ \begin{document}
+ \include{test1}
+ \include{test2}
+ \end{document}
+ "#
+ ),
+ )
+ .file("test1.tex", r#"\section{Section 1}\label{sec:1}"#)
+ .file(
+ "test2.tex",
+ indoc!(
+ r#"
+ \section{Section 2}\label{sec:2}
+ %
+ This section continues from Section \ref{sec:1}"#
+ ),
+ )
+ .build()
+ .await;
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+ test_bed.open("test2.tex").await;
+ test_bed.detect_root("test2.tex").await;
+
+ let actual_hover = test_bed.hover("test2.tex", 2, 42).await.unwrap().unwrap();
+
+ test_bed.shutdown().await;
+ assert_eq!(
+ actual_hover.contents,
+ HoverContents::Markup(MarkupContent {
+ kind: MarkupKind::PlainText,
+ value: "Section (Section 1)".into()
+ })
+ );
+}
+
+#[tokio::test]
+async fn issue_26() {
+ let mut test_bed = TestBedBuilder::new()
+ .file(
+ "main.tex",
+ indoc!(
+ r#"
+ \section{Foo}\label{sec:foo}
+ \begin{equation}\label{eq:bar}
+ \end{equation}
+ \eqref{}
+ "#
+ ),
+ )
+ .build()
+ .await;
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+ test_bed.open("main.tex").await;
+
+ let actual_items = test_bed.completion("main.tex", 3, 7).await.unwrap();
+
+ test_bed.shutdown().await;
+ assert!(actual_items.iter().any(|item| item.label == "eq:bar"));
+ assert!(actual_items.iter().all(|item| item.label != "sec:foo"));
+}
diff --git a/support/texlab/tests/integration/link.rs b/support/texlab/tests/integration/link.rs
new file mode 100644
index 0000000000..8db718bd26
--- /dev/null
+++ b/support/texlab/tests/integration/link.rs
@@ -0,0 +1,173 @@
+use indoc::indoc;
+use texlab::{
+ protocol::{DocumentLink, Range, RangeExt},
+ test::{TestBedBuilder, PULL_CAPABILITIES, PUSH_CAPABILITIES},
+};
+
+#[tokio::test]
+async fn empty_latex_document() {
+ let mut test_bed = TestBedBuilder::new().file("main.tex", "").build().await;
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+ test_bed.open("main.tex").await;
+
+ let actual_links = test_bed.document_link("main.tex").await.unwrap();
+
+ test_bed.shutdown().await;
+
+ assert!(actual_links.is_empty());
+}
+
+#[tokio::test]
+async fn default_settings() {
+ let mut test_bed = TestBedBuilder::new()
+ .file(
+ "main.tex",
+ indoc!(
+ r#"
+ \include{foo/bar}
+ \input{qux.tex}
+ "#
+ ),
+ )
+ .file("foo/bar.tex", "")
+ .file("qux.tex", "")
+ .build()
+ .await;
+
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+ test_bed.open("main.tex").await;
+
+ let actual_links = test_bed.document_link("main.tex").await.unwrap();
+
+ test_bed.shutdown().await;
+
+ let expected_links = vec![
+ DocumentLink {
+ range: Range::new_simple(0, 9, 0, 16),
+ target: test_bed.uri("foo/bar.tex").into(),
+ tooltip: None,
+ },
+ DocumentLink {
+ range: Range::new_simple(1, 7, 1, 14),
+ target: test_bed.uri("qux.tex").into(),
+ tooltip: None,
+ },
+ ];
+ assert_eq!(actual_links, expected_links);
+}
+
+#[tokio::test]
+async fn root_directory() {
+ let mut test_bed = TestBedBuilder::new()
+ .file("src/main.tex", r#"\include{src/foo}"#)
+ .file("src/foo.tex", "")
+ .root_dir(".")
+ .build()
+ .await;
+
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+ test_bed.open("src/main.tex").await;
+
+ let actual_links = test_bed.document_link("src/main.tex").await.unwrap();
+
+ test_bed.shutdown().await;
+
+ let expected_links = vec![DocumentLink {
+ range: Range::new_simple(0, 9, 0, 16),
+ target: test_bed.uri("src/foo.tex").into(),
+ tooltip: None,
+ }];
+ assert_eq!(actual_links, expected_links);
+}
+
+#[tokio::test]
+async fn parent_directory() {
+ let mut test_bed = TestBedBuilder::new()
+ .file("src/main.tex", r#"\addbibresource{../foo.bib}"#)
+ .file("foo.bib", "")
+ .build()
+ .await;
+
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+ test_bed.open("src/main.tex").await;
+
+ let actual_links = test_bed.document_link("src/main.tex").await.unwrap();
+
+ test_bed.shutdown().await;
+
+ let expected_links = vec![DocumentLink {
+ range: Range::new_simple(0, 16, 0, 26),
+ target: test_bed.uri("foo.bib").into(),
+ tooltip: None,
+ }];
+ assert_eq!(actual_links, expected_links);
+}
+
+#[tokio::test]
+async fn unknown_file() {
+ let mut test_bed = TestBedBuilder::new().build().await;
+
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+
+ let actual_links = test_bed.document_link("main.tex").await;
+
+ test_bed.shutdown().await;
+
+ assert_eq!(actual_links, None);
+}
+
+#[tokio::test]
+async fn edit_file() {
+ let mut test_bed = TestBedBuilder::new()
+ .file("main.tex", "")
+ .file("foo.tex", "")
+ .build()
+ .await;
+
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+ test_bed.open("main.tex").await;
+ test_bed.edit("main.tex", r#"\include{foo}"#).await;
+
+ let actual_links = test_bed.document_link("main.tex").await.unwrap();
+
+ test_bed.shutdown().await;
+
+ let expected_links = vec![DocumentLink {
+ range: Range::new_simple(0, 9, 0, 12),
+ target: test_bed.uri("foo.tex").into(),
+ tooltip: None,
+ }];
+ assert_eq!(actual_links, expected_links);
+}
+
+#[tokio::test]
+async fn did_change_configuration() {
+ let mut test_bed = TestBedBuilder::new()
+ .file("src/main.tex", r#"\include{src/foo}"#)
+ .file("src/foo.tex", "")
+ .root_dir(".")
+ .build()
+ .await;
+
+ test_bed.spawn();
+ test_bed.initialize(PUSH_CAPABILITIES.clone()).await;
+ test_bed.open("src/main.tex").await;
+ test_bed.push_options().await;
+
+ let actual_links = test_bed.document_link("src/main.tex").await.unwrap();
+
+ test_bed.shutdown().await;
+
+ let expected_links = vec![DocumentLink {
+ range: Range::new_simple(0, 9, 0, 16),
+ target: test_bed.uri("src/foo.tex").into(),
+ tooltip: None,
+ }];
+ assert_eq!(actual_links, expected_links);
+}
diff --git a/support/texlab/tests/integration/main.rs b/support/texlab/tests/integration/main.rs
new file mode 100644
index 0000000000..99c0b23a5c
--- /dev/null
+++ b/support/texlab/tests/integration/main.rs
@@ -0,0 +1,12 @@
+mod completion;
+mod definition;
+mod document_symbol;
+mod folding;
+mod highlight;
+mod hover;
+mod issues;
+mod link;
+mod prepare_rename;
+mod reference;
+mod rename;
+mod workspace_symbol;
diff --git a/support/texlab/tests/integration/prepare_rename.rs b/support/texlab/tests/integration/prepare_rename.rs
new file mode 100644
index 0000000000..0efaa712b2
--- /dev/null
+++ b/support/texlab/tests/integration/prepare_rename.rs
@@ -0,0 +1,151 @@
+use texlab::{
+ protocol::{Range, RangeExt},
+ test::{TestBedBuilder, PULL_CAPABILITIES},
+};
+
+#[tokio::test]
+async fn empty_latex_document() {
+ let mut test_bed = TestBedBuilder::new().file("main.tex", "").build().await;
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+ test_bed.open("main.tex").await;
+
+ let actual_range = test_bed.prepare_rename("main.tex", 0, 0).await.unwrap();
+
+ test_bed.shutdown().await;
+
+ assert_eq!(actual_range, None);
+}
+
+#[tokio::test]
+async fn empty_bibtex_document() {
+ let mut test_bed = TestBedBuilder::new().file("main.bib", "").build().await;
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+ test_bed.open("main.bib").await;
+
+ let actual_range = test_bed.prepare_rename("main.bib", 0, 0).await.unwrap();
+
+ test_bed.shutdown().await;
+
+ assert_eq!(actual_range, None);
+}
+
+#[tokio::test]
+async fn bibtex_entry() {
+ let mut test_bed = TestBedBuilder::new()
+ .file("main.bib", r#"@article{foo,}"#)
+ .build()
+ .await;
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+ test_bed.open("main.bib").await;
+
+ let actual_range = test_bed
+ .prepare_rename("main.bib", 0, 10)
+ .await
+ .unwrap()
+ .unwrap();
+
+ test_bed.shutdown().await;
+
+ assert_eq!(actual_range, Range::new_simple(0, 9, 0, 12));
+}
+
+#[tokio::test]
+async fn latex_citation() {
+ let mut test_bed = TestBedBuilder::new()
+ .file("main.tex", r#"\cite{foo,bar}"#)
+ .build()
+ .await;
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+ test_bed.open("main.tex").await;
+
+ let actual_range = test_bed
+ .prepare_rename("main.tex", 0, 11)
+ .await
+ .unwrap()
+ .unwrap();
+
+ test_bed.shutdown().await;
+
+ assert_eq!(actual_range, Range::new_simple(0, 10, 0, 13));
+}
+
+#[tokio::test]
+async fn latex_command() {
+ let mut test_bed = TestBedBuilder::new()
+ .file("main.tex", r#"\foo"#)
+ .build()
+ .await;
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+ test_bed.open("main.tex").await;
+
+ let actual_range = test_bed
+ .prepare_rename("main.tex", 0, 1)
+ .await
+ .unwrap()
+ .unwrap();
+
+ test_bed.shutdown().await;
+
+ assert_eq!(actual_range, Range::new_simple(0, 0, 0, 4));
+}
+
+#[tokio::test]
+async fn latex_environment() {
+ let mut test_bed = TestBedBuilder::new()
+ .file("main.tex", r#"\begin{foo}\end{bar}"#)
+ .build()
+ .await;
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+ test_bed.open("main.tex").await;
+
+ let actual_range = test_bed
+ .prepare_rename("main.tex", 0, 7)
+ .await
+ .unwrap()
+ .unwrap();
+
+ test_bed.shutdown().await;
+
+ assert_eq!(actual_range, Range::new_simple(0, 7, 0, 10));
+}
+
+#[tokio::test]
+async fn latex_label() {
+ let mut test_bed = TestBedBuilder::new()
+ .file("main.tex", r#"\ref{foo,bar}"#)
+ .build()
+ .await;
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+ test_bed.open("main.tex").await;
+
+ let actual_range = test_bed
+ .prepare_rename("main.tex", 0, 9)
+ .await
+ .unwrap()
+ .unwrap();
+
+ test_bed.shutdown().await;
+
+ assert_eq!(actual_range, Range::new_simple(0, 9, 0, 12));
+}
+
+#[tokio::test]
+async fn unknown_file() {
+ let mut test_bed = TestBedBuilder::new().build().await;
+
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+
+ let actual_range = test_bed.prepare_rename("main.tex", 0, 0).await;
+
+ test_bed.shutdown().await;
+
+ assert_eq!(actual_range, None);
+}
diff --git a/support/texlab/tests/integration/reference.rs b/support/texlab/tests/integration/reference.rs
new file mode 100644
index 0000000000..224f85fbfc
--- /dev/null
+++ b/support/texlab/tests/integration/reference.rs
@@ -0,0 +1,150 @@
+use indoc::indoc;
+use texlab::{
+ protocol::{Location, Range, RangeExt},
+ test::{TestBedBuilder, PULL_CAPABILITIES},
+};
+
+#[tokio::test]
+async fn empty_latex_document() {
+ let mut test_bed = TestBedBuilder::new().file("main.tex", "").build().await;
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+ test_bed.open("main.tex").await;
+
+ let actual_refs = test_bed.references("main.tex", 0, 0, false).await.unwrap();
+
+ test_bed.shutdown().await;
+
+ assert!(actual_refs.is_empty());
+}
+
+#[tokio::test]
+async fn empty_bibtex_document() {
+ let mut test_bed = TestBedBuilder::new().file("main.bib", "").build().await;
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+ test_bed.open("main.bib").await;
+
+ let actual_refs = test_bed.references("main.bib", 0, 0, false).await.unwrap();
+
+ test_bed.shutdown().await;
+
+ assert!(actual_refs.is_empty());
+}
+
+#[tokio::test]
+async fn bibtex_entry() {
+ let mut test_bed = TestBedBuilder::new()
+ .file("main.bib", r#"@article{foo,}"#)
+ .file(
+ "main.tex",
+ indoc!(
+ r#"
+ \addbibresource{main.bib}
+ \cite{foo}
+ "#
+ ),
+ )
+ .build()
+ .await;
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+ test_bed.open("main.bib").await;
+ test_bed.open("main.tex").await;
+
+ let actual_refs = test_bed.references("main.tex", 1, 8, true).await.unwrap();
+
+ test_bed.shutdown().await;
+
+ let expected_refs = vec![
+ Location {
+ uri: test_bed.uri("main.tex").into(),
+ range: Range::new_simple(1, 6, 1, 9),
+ },
+ Location {
+ uri: test_bed.uri("main.bib").into(),
+ range: Range::new_simple(0, 9, 0, 12),
+ },
+ ];
+
+ assert_eq!(actual_refs, expected_refs);
+}
+
+#[tokio::test]
+async fn bibtex_string() {
+ let mut test_bed = TestBedBuilder::new()
+ .file(
+ "main.bib",
+ indoc!(
+ r#"
+ @string{foo = "foo"}
+ @article{bar, author = foo # foo}
+ "#
+ ),
+ )
+ .build()
+ .await;
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+ test_bed.open("main.bib").await;
+
+ let actual_refs = test_bed.references("main.bib", 1, 31, false).await.unwrap();
+
+ test_bed.shutdown().await;
+
+ let expected_refs = vec![
+ Location {
+ uri: test_bed.uri("main.bib").into(),
+ range: Range::new_simple(1, 23, 1, 26),
+ },
+ Location {
+ uri: test_bed.uri("main.bib").into(),
+ range: Range::new_simple(1, 29, 1, 32),
+ },
+ ];
+
+ assert_eq!(actual_refs, expected_refs);
+}
+
+#[tokio::test]
+async fn latex_label() {
+ let mut test_bed = TestBedBuilder::new()
+ .file(
+ "main.tex",
+ indoc!(
+ r#"
+ \label{foo}
+ \ref{foo}
+ "#
+ ),
+ )
+ .build()
+ .await;
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+ test_bed.open("main.tex").await;
+
+ let actual_refs = test_bed.references("main.tex", 0, 7, false).await.unwrap();
+
+ test_bed.shutdown().await;
+
+ let expected_refs = vec![Location {
+ uri: test_bed.uri("main.tex").into(),
+ range: Range::new_simple(1, 5, 1, 8),
+ }];
+
+ assert_eq!(actual_refs, expected_refs);
+}
+
+#[tokio::test]
+async fn unknown_file() {
+ let mut test_bed = TestBedBuilder::new().build().await;
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+
+ let actual_refs = test_bed.references("main.tex", 0, 0, false).await;
+
+ test_bed.shutdown().await;
+
+ assert_eq!(actual_refs, None);
+}
diff --git a/support/texlab/tests/integration/rename.rs b/support/texlab/tests/integration/rename.rs
new file mode 100644
index 0000000000..d8522f1ec6
--- /dev/null
+++ b/support/texlab/tests/integration/rename.rs
@@ -0,0 +1,236 @@
+use indoc::indoc;
+use std::collections::HashMap;
+use texlab::{
+ protocol::{Range, RangeExt, TextEdit, WorkspaceEdit},
+ test::{TestBedBuilder, PULL_CAPABILITIES},
+};
+
+#[tokio::test]
+async fn empty_latex_document() {
+ let mut test_bed = TestBedBuilder::new().file("main.tex", "").build().await;
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+ test_bed.open("main.tex").await;
+
+ let actual_edit = test_bed.rename("main.tex", 0, 0, "").await.unwrap();
+
+ test_bed.shutdown().await;
+
+ assert_eq!(actual_edit, None);
+}
+
+#[tokio::test]
+async fn empty_bibtex_document() {
+ let mut test_bed = TestBedBuilder::new().file("main.bib", "").build().await;
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+ test_bed.open("main.bib").await;
+
+ let actual_edit = test_bed.rename("main.bib", 0, 0, "").await.unwrap();
+
+ test_bed.shutdown().await;
+
+ assert_eq!(actual_edit, None);
+}
+
+#[tokio::test]
+async fn bibtex_entry() {
+ let mut test_bed = TestBedBuilder::new()
+ .file(
+ "main.bib",
+ indoc!(
+ r#"
+ @article{foo,}
+ @article{bar,}
+ "#
+ ),
+ )
+ .file(
+ "main.tex",
+ indoc!(
+ r#"
+ \addbibresource{main.bib}
+ \cite{foo,bar}
+ "#
+ ),
+ )
+ .build()
+ .await;
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+ test_bed.open("main.bib").await;
+ test_bed.open("main.tex").await;
+
+ let actual_edit = test_bed
+ .rename("main.bib", 1, 10, "baz")
+ .await
+ .unwrap()
+ .unwrap();
+
+ test_bed.shutdown().await;
+
+ let mut expected_changes = HashMap::new();
+ expected_changes.insert(
+ test_bed.uri("main.bib").into(),
+ vec![TextEdit::new(Range::new_simple(1, 9, 1, 12), "baz".into())],
+ );
+ expected_changes.insert(
+ test_bed.uri("main.tex").into(),
+ vec![TextEdit::new(Range::new_simple(1, 10, 1, 13), "baz".into())],
+ );
+
+ assert_eq!(actual_edit, WorkspaceEdit::new(expected_changes));
+}
+
+#[tokio::test]
+async fn latex_command() {
+ let mut test_bed = TestBedBuilder::new()
+ .file(
+ "foo.tex",
+ indoc!(
+ r#"
+ \input{bar.tex}
+ \foo
+ "#
+ ),
+ )
+ .file(
+ "bar.tex",
+ indoc!(
+ r#"
+ \foo
+ \bar
+ "#
+ ),
+ )
+ .build()
+ .await;
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+ test_bed.open("foo.tex").await;
+ test_bed.open("bar.tex").await;
+
+ let actual_edit = test_bed
+ .rename("foo.tex", 1, 2, "baz")
+ .await
+ .unwrap()
+ .unwrap();
+
+ test_bed.shutdown().await;
+
+ let mut expected_changes = HashMap::new();
+ expected_changes.insert(
+ test_bed.uri("foo.tex").into(),
+ vec![TextEdit::new(Range::new_simple(1, 0, 1, 4), "\\baz".into())],
+ );
+ expected_changes.insert(
+ test_bed.uri("bar.tex").into(),
+ vec![TextEdit::new(Range::new_simple(0, 0, 0, 4), "\\baz".into())],
+ );
+
+ assert_eq!(actual_edit, WorkspaceEdit::new(expected_changes));
+}
+
+#[tokio::test]
+async fn latex_environment() {
+ let mut test_bed = TestBedBuilder::new()
+ .file(
+ "main.tex",
+ indoc!(
+ r#"
+ \begin{foo}
+ \begin{bar}
+ Baz
+ \end{bar}
+ \end{foo}
+ "#
+ ),
+ )
+ .build()
+ .await;
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+ test_bed.open("main.tex").await;
+
+ let actual_edit = test_bed
+ .rename("main.tex", 3, 11, "baz")
+ .await
+ .unwrap()
+ .unwrap();
+
+ test_bed.shutdown().await;
+
+ let mut expected_changes = HashMap::new();
+ expected_changes.insert(
+ test_bed.uri("main.tex").into(),
+ vec![
+ TextEdit::new(Range::new_simple(1, 11, 1, 14), "baz".into()),
+ TextEdit::new(Range::new_simple(3, 9, 3, 12), "baz".into()),
+ ],
+ );
+
+ assert_eq!(actual_edit, WorkspaceEdit::new(expected_changes));
+}
+
+#[tokio::test]
+async fn latex_label() {
+ let mut test_bed = TestBedBuilder::new()
+ .file(
+ "foo.tex",
+ indoc!(
+ r#"
+ \input{bar.tex}
+ \ref{foo,bar}
+ "#
+ ),
+ )
+ .file(
+ "bar.tex",
+ indoc!(
+ r#"
+ \label{foo}
+ \label{bar}
+ "#
+ ),
+ )
+ .build()
+ .await;
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+ test_bed.open("foo.tex").await;
+ test_bed.open("bar.tex").await;
+
+ let actual_edit = test_bed
+ .rename("foo.tex", 1, 10, "baz")
+ .await
+ .unwrap()
+ .unwrap();
+
+ test_bed.shutdown().await;
+
+ let mut expected_changes = HashMap::new();
+ expected_changes.insert(
+ test_bed.uri("foo.tex").into(),
+ vec![TextEdit::new(Range::new_simple(1, 9, 1, 12), "baz".into())],
+ );
+ expected_changes.insert(
+ test_bed.uri("bar.tex").into(),
+ vec![TextEdit::new(Range::new_simple(1, 7, 1, 10), "baz".into())],
+ );
+
+ assert_eq!(actual_edit, WorkspaceEdit::new(expected_changes));
+}
+
+#[tokio::test]
+async fn unknown_file() {
+ let mut test_bed = TestBedBuilder::new().build().await;
+
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+
+ let actual_edit = test_bed.rename("main.tex", 0, 0, "").await;
+
+ test_bed.shutdown().await;
+
+ assert_eq!(actual_edit, None);
+}
diff --git a/support/texlab/tests/integration/workspace_symbol.rs b/support/texlab/tests/integration/workspace_symbol.rs
new file mode 100644
index 0000000000..5eb8e1ace7
--- /dev/null
+++ b/support/texlab/tests/integration/workspace_symbol.rs
@@ -0,0 +1,228 @@
+use indoc::indoc;
+use texlab::{
+ protocol::{Location, Range, RangeExt, SymbolInformation, WorkspaceSymbolParams},
+ test::{TestBed, TestBedBuilder, TestLspClient, PULL_CAPABILITIES},
+};
+
+pub fn verify_symbol_info(
+ symbol: &SymbolInformation,
+ test_bed: &TestBed,
+ relative_path: &str,
+ name: &str,
+ start_line: u64,
+ start_character: u64,
+ end_line: u64,
+ end_character: u64,
+) {
+ assert_eq!(symbol.name, name);
+ let range = Range::new_simple(start_line, start_character, end_line, end_character);
+ assert_eq!(
+ symbol.location,
+ Location::new(test_bed.uri(relative_path).into(), range)
+ );
+}
+
+async fn run(query: &str) -> (TestBed, Vec<SymbolInformation>) {
+ let mut test_bed = TestBedBuilder::new()
+ .file(
+ "foo.tex",
+ indoc!(
+ r#"
+ \documentclass{article}
+ \usepackage{caption}
+ \usepackage{amsmath}
+ \usepackage{amsthm}
+ %
+ \begin{document}
+ %
+ \section{Foo}\label{sec:foo}
+ %
+ \begin{equation}\label{eq:foo}
+ Foo
+ \end{equation}
+ %
+ \section{Bar}\label{sec:bar}
+ %
+ \begin{figure}
+ Bar
+ \caption{Bar}
+ \label{fig:bar}
+ \end{figure}
+ %
+ \section{Baz}\label{sec:baz}
+ %
+ \begin{enumerate}
+ \item\label{itm:foo} Foo
+ \item\label{itm:bar} Bar
+ \item\label{itm:baz} Baz
+ \end{enumerate}
+ %
+ \section{Qux}\label{sec:qux}
+ %
+ \newtheorem{lemma}{Lemma}
+ %
+ \begin{lemma}[Qux]\label{thm:qux}
+ Qux
+ \end{lemma}
+ %
+ \end{document}
+ "#
+ ),
+ )
+ .file(
+ "foo.aux",
+ indoc!(
+ r#"
+ \relax
+ \@writefile{lof}{\contentsline {figure}{\numberline {1}{\ignorespaces Bar\relax }}{1}\protected@file@percent }
+ \providecommand*\caption@xref[2]{\@setref\relax\@undefined{#1}}
+ \newlabel{fig:bar}{{1}{1}}
+ \@writefile{toc}{\contentsline {section}{\numberline {1}Foo}{1}\protected@file@percent }
+ \newlabel{sec:foo}{{1}{1}}
+ \newlabel{eq:foo}{{1}{1}}
+ \@writefile{toc}{\contentsline {section}{\numberline {2}Bar}{1}\protected@file@percent }
+ \newlabel{sec:bar}{{2}{1}}
+ \@writefile{toc}{\contentsline {section}{\numberline {3}Baz}{1}\protected@file@percent }
+ \newlabel{sec:baz}{{3}{1}}
+ \newlabel{itm:foo}{{1}{1}}
+ \newlabel{itm:bar}{{2}{1}}
+ \newlabel{itm:baz}{{3}{1}}
+ \@writefile{toc}{\contentsline {section}{\numberline {4}Qux}{1}\protected@file@percent }
+ \newlabel{sec:qux}{{4}{1}}
+ \newlabel{thm:qux}{{1}{1}}
+ "#
+ ),
+ )
+ .file(
+ "bar.bib",
+ indoc!(
+ r#"
+ @article{foo,}
+ %
+ @string{bar = "bar"}
+ "#
+ ),
+ )
+ .build()
+ .await;
+ test_bed.spawn();
+ test_bed.initialize(PULL_CAPABILITIES.clone()).await;
+ test_bed.open("foo.tex").await;
+ test_bed.open("foo.aux").await;
+ test_bed.open("bar.bib").await;
+
+ let params = WorkspaceSymbolParams {
+ query: query.into(),
+ ..WorkspaceSymbolParams::default()
+ };
+ let actual_symbols = test_bed.client.workspace_symbol(params).await.unwrap();
+
+ test_bed.shutdown().await;
+
+ (test_bed, actual_symbols)
+}
+
+#[tokio::test]
+async fn filter_type_section() {
+ let (test_bed, actual_symbols) = run("section").await;
+ assert_eq!(actual_symbols.len(), 4);
+ verify_symbol_info(
+ &actual_symbols[0],
+ &test_bed,
+ "foo.tex",
+ "1 Foo",
+ 7,
+ 0,
+ 13,
+ 0,
+ );
+ verify_symbol_info(
+ &actual_symbols[1],
+ &test_bed,
+ "foo.tex",
+ "2 Bar",
+ 13,
+ 0,
+ 21,
+ 0,
+ );
+ verify_symbol_info(
+ &actual_symbols[2],
+ &test_bed,
+ "foo.tex",
+ "3 Baz",
+ 21,
+ 0,
+ 29,
+ 0,
+ );
+ verify_symbol_info(
+ &actual_symbols[3],
+ &test_bed,
+ "foo.tex",
+ "4 Qux",
+ 29,
+ 0,
+ 37,
+ 0,
+ );
+}
+
+#[tokio::test]
+async fn filter_type_figure() {
+ let (test_bed, actual_symbols) = run("figure").await;
+ assert_eq!(actual_symbols.len(), 1);
+ verify_symbol_info(
+ &actual_symbols[0],
+ &test_bed,
+ "foo.tex",
+ "Figure 1: Bar",
+ 15,
+ 0,
+ 19,
+ 12,
+ );
+}
+
+#[tokio::test]
+async fn filter_type_item() {
+ let (test_bed, actual_symbols) = run("item").await;
+ assert_eq!(actual_symbols.len(), 3);
+ verify_symbol_info(&actual_symbols[0], &test_bed, "foo.tex", "1", 24, 4, 25, 4);
+ verify_symbol_info(&actual_symbols[1], &test_bed, "foo.tex", "2", 25, 4, 26, 4);
+ verify_symbol_info(&actual_symbols[2], &test_bed, "foo.tex", "3", 26, 4, 27, 0);
+}
+
+#[tokio::test]
+async fn filter_type_math() {
+ let (test_bed, actual_symbols) = run("math").await;
+ assert_eq!(actual_symbols.len(), 2);
+ verify_symbol_info(
+ &actual_symbols[0],
+ &test_bed,
+ "foo.tex",
+ "Equation (1)",
+ 9,
+ 0,
+ 11,
+ 14,
+ );
+ verify_symbol_info(
+ &actual_symbols[1],
+ &test_bed,
+ "foo.tex",
+ "Lemma 1 (Qux)",
+ 33,
+ 0,
+ 35,
+ 11,
+ );
+}
+
+#[tokio::test]
+async fn filter_bibtex() {
+ let (test_bed, actual_symbols) = run("bibtex").await;
+ assert_eq!(actual_symbols.len(), 2);
+ verify_symbol_info(&actual_symbols[0], &test_bed, "bar.bib", "foo", 0, 0, 0, 14);
+ verify_symbol_info(&actual_symbols[1], &test_bed, "bar.bib", "bar", 2, 0, 2, 20);
+}