diff options
Diffstat (limited to 'support/texlab/tests/integration')
-rw-r--r-- | support/texlab/tests/integration/completion.rs | 1227 | ||||
-rw-r--r-- | support/texlab/tests/integration/definition.rs | 482 | ||||
-rw-r--r-- | support/texlab/tests/integration/document_symbol.rs | 424 | ||||
-rw-r--r-- | support/texlab/tests/integration/folding.rs | 126 | ||||
-rw-r--r-- | support/texlab/tests/integration/highlight.rs | 83 | ||||
-rw-r--r-- | support/texlab/tests/integration/hover.rs | 86 | ||||
-rw-r--r-- | support/texlab/tests/integration/issues.rs | 350 | ||||
-rw-r--r-- | support/texlab/tests/integration/link.rs | 173 | ||||
-rw-r--r-- | support/texlab/tests/integration/main.rs | 12 | ||||
-rw-r--r-- | support/texlab/tests/integration/prepare_rename.rs | 151 | ||||
-rw-r--r-- | support/texlab/tests/integration/reference.rs | 150 | ||||
-rw-r--r-- | support/texlab/tests/integration/rename.rs | 236 | ||||
-rw-r--r-- | support/texlab/tests/integration/workspace_symbol.rs | 228 |
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); +} |