summaryrefslogtreecommitdiff
path: root/support/texlab/tests/lsp/text_document
diff options
context:
space:
mode:
Diffstat (limited to 'support/texlab/tests/lsp/text_document')
-rw-r--r--support/texlab/tests/lsp/text_document/completion.rs821
-rw-r--r--support/texlab/tests/lsp/text_document/definition.rs156
-rw-r--r--support/texlab/tests/lsp/text_document/document_highlight.rs51
-rw-r--r--support/texlab/tests/lsp/text_document/document_link.rs52
-rw-r--r--support/texlab/tests/lsp/text_document/document_symbol.rs441
-rw-r--r--support/texlab/tests/lsp/text_document/folding_range.rs66
-rw-r--r--support/texlab/tests/lsp/text_document/formatting.rs134
-rw-r--r--support/texlab/tests/lsp/text_document/hover.rs246
-rw-r--r--support/texlab/tests/lsp/text_document/inlay_hint.rs57
-rw-r--r--support/texlab/tests/lsp/text_document/references.rs270
-rw-r--r--support/texlab/tests/lsp/text_document/rename.rs106
-rw-r--r--support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__acronym_package_ref.snap10
-rw-r--r--support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__acronym_ref_empty.snap10
-rw-r--r--support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__acronym_ref_open_brace.snap10
-rw-r--r--support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__acronym_ref_simple.snap10
-rw-r--r--support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__argument_empty.snap26
-rw-r--r--support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__argument_open_brace.snap26
-rw-r--r--support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__argument_word.snap10
-rw-r--r--support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__begin_environment_without_snippet_support.snap31
-rw-r--r--support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__citation.snap16
-rw-r--r--support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__citation_acronym.snap11
-rw-r--r--support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__citation_open_brace.snap11
-rw-r--r--support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__citation_open_brace_multiple.snap11
-rw-r--r--support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__color_model_definition_open_brace.snap26
-rw-r--r--support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__color_model_definition_set_open_brace.snap26
-rw-r--r--support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__color_model_definition_set_simple.snap26
-rw-r--r--support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__color_model_definition_simple.snap26
-rw-r--r--support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__color_open_brace.snap26
-rw-r--r--support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__color_simple.snap26
-rw-r--r--support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__color_word.snap26
-rw-r--r--support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__component_command_bibtex.snap31
-rw-r--r--support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__component_command_simple.snap31
-rw-r--r--support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__component_command_simple_package.snap31
-rw-r--r--support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__component_environment_class.snap11
-rw-r--r--support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__component_environment_command_definition.snap11
-rw-r--r--support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__component_environment_simple.snap11
-rw-r--r--support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__component_environment_simple_end.snap31
-rw-r--r--support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__entry_type_after_preamble.snap10
-rw-r--r--support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__entry_type_after_string.snap10
-rw-r--r--support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__entry_type_at_empty.snap26
-rw-r--r--support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__entry_type_before_article.snap10
-rw-r--r--support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__entry_type_before_preamble.snap10
-rw-r--r--support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__entry_type_before_string.snap10
-rw-r--r--support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__entry_type_complete_entry.snap10
-rw-r--r--support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__field_empty_entry_closed.snap26
-rw-r--r--support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__field_empty_entry_open.snap26
-rw-r--r--support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__field_entry_field_name.snap26
-rw-r--r--support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__field_entry_two_fields_name_closed.snap26
-rw-r--r--support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__field_entry_two_fields_name_open.snap26
-rw-r--r--support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__glossary_ref_open_brace.snap10
-rw-r--r--support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__glossary_ref_simple.snap10
-rw-r--r--support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__import_class_closed_brace.snap26
-rw-r--r--support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__import_class_open_brace.snap26
-rw-r--r--support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__import_package_closed_brace.snap26
-rw-r--r--support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__import_package_open_brace.snap26
-rw-r--r--support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__label.snap36
-rw-r--r--support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__project_resolution_import.snap31
-rw-r--r--support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__project_resolution_texlabroot.snap31
-rw-r--r--support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__theorem_begin.snap11
-rw-r--r--support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__theorem_end.snap11
-rw-r--r--support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__tikz_library_closed_brace.snap26
-rw-r--r--support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__tikz_library_open_brace.snap26
-rw-r--r--support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__user_command.snap11
-rw-r--r--support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__user_environment.snap16
-rw-r--r--support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__document_link__document_import.snap19
-rw-r--r--support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__document_link__document_include.snap19
-rw-r--r--support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__document_symbol__enumerate_flat.snap96
-rw-r--r--support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__document_symbol__enumerate_nested.snap140
-rw-r--r--support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__document_symbol__equation_flat.snap60
-rw-r--r--support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__document_symbol__equation_nested.snap86
-rw-r--r--support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__document_symbol__float_flat.snap60
-rw-r--r--support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__document_symbol__float_nested.snap86
-rw-r--r--support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__document_symbol__ignored_patterns.snap24
-rw-r--r--support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__document_symbol__section_flat.snap60
-rw-r--r--support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__document_symbol__section_nested.snap87
-rw-r--r--support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__document_symbol__theorem_flat.snap78
-rw-r--r--support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__document_symbol__theorem_nested.snap113
-rw-r--r--support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__folding_range__bibtex.snap27
-rw-r--r--support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__folding_range__latex.snap41
-rw-r--r--support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__formatting__bibtex_internal_command.snap7
-rw-r--r--support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__formatting__bibtex_internal_insert_braces.snap7
-rw-r--r--support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__formatting__bibtex_internal_join_strings.snap7
-rw-r--r--support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__formatting__bibtex_internal_multiple_entries.snap15
-rw-r--r--support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__formatting__bibtex_internal_parens.snap6
-rw-r--r--support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__formatting__bibtex_internal_preamble.snap5
-rw-r--r--support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__formatting__bibtex_internal_string.snap5
-rw-r--r--support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__formatting__bibtex_internal_trailing_comma.snap7
-rw-r--r--support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__formatting__bibtex_internal_wrap_long_lines.snap9
-rw-r--r--support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__hover__citation_inside_cite.snap8
-rw-r--r--support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__hover__citation_inside_entry.snap8
-rw-r--r--support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__hover__component_known_package.snap8
-rw-r--r--support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__hover__entry_type_known_type.snap8
-rw-r--r--support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__hover__field_known.snap8
-rw-r--r--support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__hover__label_theorem_child_file.snap8
-rw-r--r--support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__hover__label_theorem_child_file_mumber.snap8
-rw-r--r--support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__hover__section.snap8
-rw-r--r--support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__hover__string_inside_reference.snap8
-rw-r--r--support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__inlay_hint__label_definition.snap38
98 files changed, 4634 insertions, 0 deletions
diff --git a/support/texlab/tests/lsp/text_document/completion.rs b/support/texlab/tests/lsp/text_document/completion.rs
new file mode 100644
index 0000000000..7e0c3c31d4
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/completion.rs
@@ -0,0 +1,821 @@
+use insta::assert_json_snapshot;
+use itertools::Itertools;
+use lsp_types::{
+ request::{Completion, ResolveCompletionItem},
+ CompletionItem, CompletionParams, CompletionResponse, CompletionTextEdit, Range,
+};
+
+use crate::fixture::TestBed;
+
+fn complete(fixture: &str) -> Vec<CompletionItem> {
+ let test_bed = TestBed::new(fixture).unwrap();
+ test_bed
+ .initialize(
+ serde_json::from_value(serde_json::json!({
+ "textDocument": {
+ "completion": {
+ "completionItem": {
+ "documentationFormat": ["plaintext", "markdown"]
+ }
+ }
+ }
+ }))
+ .unwrap(),
+ )
+ .unwrap();
+
+ let text_document_position = test_bed.cursor().unwrap();
+ let position = text_document_position.position;
+ let range = test_bed
+ .locations()
+ .first()
+ .map_or_else(|| Range::new(position, position), |location| location.range);
+
+ let items = match test_bed
+ .client()
+ .send_request::<Completion>(CompletionParams {
+ text_document_position,
+ partial_result_params: Default::default(),
+ work_done_progress_params: Default::default(),
+ context: None,
+ })
+ .unwrap()
+ {
+ Some(CompletionResponse::Array(items)) => items,
+ Some(CompletionResponse::List(list)) => list.items,
+ None => Vec::new(),
+ };
+
+ for item in &items {
+ if let Some(CompletionTextEdit::Edit(edit)) = item.text_edit.as_ref() {
+ assert_eq!(edit.range, range);
+ }
+ }
+
+ items
+ .into_iter()
+ .take(5)
+ .map(|item| {
+ let mut item = test_bed
+ .client()
+ .send_request::<ResolveCompletionItem>(item)
+ .unwrap();
+
+ item.data = None;
+ item.kind = None;
+ item.sort_text = None;
+ item.documentation = None;
+ item.text_edit = None;
+ item
+ })
+ .sorted_by(|item1, item2| item1.label.cmp(&item2.label))
+ .collect()
+}
+
+#[test]
+fn acronym_ref_simple() {
+ assert_json_snapshot!(complete(
+ r#"
+%! main.tex
+\newacronym[longplural={Frames per Second}]{fpsLabel}{FPS}{Frame per Second}
+\acrshort{f}
+ |
+ ^"#
+ ));
+}
+
+#[test]
+fn acronym_ref_empty() {
+ assert_json_snapshot!(complete(
+ r#"
+%! main.tex
+\newacronym[longplural={Frames per Second}]{fpsLabel}{FPS}{Frame per Second}
+\acrshort{}
+ |"#
+ ));
+}
+
+#[test]
+fn acronym_ref_after_group() {
+ assert_eq!(
+ complete(
+ r#"
+%! main.tex
+\newacronym[longplural={Frames per Second}]{fpsLabel}{FPS}{Frame per Second}
+\acrshort{}
+ |"#,
+ ),
+ Vec::new()
+ );
+}
+
+#[test]
+fn acronym_ref_open_brace() {
+ assert_json_snapshot!(complete(
+ r#"
+%! main.tex
+\newacronym[longplural={Frames per Second}]{fpsLabel}{FPS}{Frame per Second}
+\acrshort{f
+ |
+ ^"#
+ ));
+}
+
+#[test]
+fn acronym_package_ref() {
+ assert_json_snapshot!(complete(
+ r#"
+%! main.tex
+\acrodef{fpsLabel}[FPS]{Frames per Second}
+\ac{f
+ |
+ ^"#
+ ));
+}
+
+#[test]
+fn glossary_ref_simple() {
+ assert_json_snapshot!(complete(
+ r#"
+%! main.tex
+\newacronym[longplural={Frames per Second}]{fpsLabel}{FPS}{Frame per Second}
+\gls{f}
+ |
+ ^"#
+ ));
+}
+
+#[test]
+fn glossary_ref_open_brace() {
+ assert_json_snapshot!(complete(
+ r#"
+%! main.tex
+\newacronym[longplural={Frames per Second}]{fpsLabel}{FPS}{Frame per Second}
+\gls{f
+ |
+ ^"#
+ ));
+}
+
+#[test]
+fn argument_empty() {
+ assert_json_snapshot!(complete(
+ r#"
+%! main.tex
+\usepackage{amsfonts}
+\mathbb{}
+ |"#
+ ));
+}
+
+#[test]
+fn argument_word() {
+ assert_json_snapshot!(complete(
+ r#"
+%! main.tex
+\usepackage{amsfonts}
+\mathbb{A}
+ |
+ ^"#
+ ));
+}
+
+#[test]
+fn argument_open_brace() {
+ assert_json_snapshot!(complete(
+ r#"
+%! main.tex
+\usepackage{amsfonts}
+\mathbb{
+ |
+Test"#
+ ));
+}
+
+#[test]
+fn argument_open_brace_unrelated() {
+ assert_eq!(
+ complete(
+ r#"
+%! main.tex
+\usepackage{amsfonts}
+\mathbb{}{
+ |
+Test"#,
+ ),
+ Vec::new()
+ );
+}
+
+#[test]
+fn begin_environment_without_snippet_support() {
+ assert_json_snapshot!(complete(
+ r#"
+%! main.tex
+\beg
+ |
+ ^^^"#
+ ));
+}
+
+#[test]
+fn citation() {
+ assert_json_snapshot!(complete(
+ r#"
+%! main.tex
+\documentclass{article}
+\bibliography{main}
+\begin{document}
+\cite{
+ |
+\end{document}
+
+%! main.bib
+@article{foo:2019,
+ author = {Foo Bar},
+ title = {Baz Qux},
+ year = {2019},
+}
+
+@article{bar:2005,}"#
+ ));
+}
+
+#[test]
+fn citation_open_brace() {
+ assert_json_snapshot!(complete(
+ r#"
+%! main.tex
+\addbibresource{main.bib}
+\cite{
+ |
+
+%! main.bib
+@article{foo,}"#
+ ));
+}
+
+#[test]
+fn citation_open_brace_multiple() {
+ assert_json_snapshot!(complete(
+ r#"
+%! main.tex
+\addbibresource{main.bib}
+\cite{foo,a
+ |
+ ^
+
+%! main.bib
+@article{foo,}"#
+ ));
+}
+
+#[test]
+fn citation_acronym() {
+ assert_json_snapshot!(complete(
+ r#"
+%! main.tex
+\addbibresource{main.bib}
+\DeclareAcronym{foo}{cite={}}
+ |
+
+%! main.bib
+@article{foo,}"#
+ ));
+}
+
+#[test]
+fn citation_after_brace() {
+ assert_eq!(
+ complete(
+ r#"
+%! main.tex
+\documentclass{article}
+\bibliography{main}
+\begin{document}
+\cite{}
+ |
+\end{document}
+
+%! main.bib
+@article{foo,}"#,
+ ),
+ Vec::new()
+ );
+}
+
+#[test]
+fn color_model_definition_simple() {
+ assert_json_snapshot!(complete(
+ r#"
+%! main.tex
+\definecolor{foo}{}
+ |"#
+ ));
+}
+
+#[test]
+fn color_model_definition_open_brace() {
+ assert_json_snapshot!(complete(
+ r#"
+%! main.tex
+\definecolor{foo}{
+ |"#
+ ));
+}
+
+#[test]
+fn color_model_definition_set_simple() {
+ assert_json_snapshot!(complete(
+ r#"
+%! main.tex
+\definecolorset{}
+ |"#
+ ));
+}
+
+#[test]
+fn color_model_definition_set_open_brace() {
+ assert_json_snapshot!(complete(
+ r#"
+%! main.tex
+\definecolorset{
+ |"#
+ ));
+}
+
+#[test]
+fn color_simple() {
+ assert_json_snapshot!(complete(
+ r#"
+%! main.tex
+\color{}
+ |"#
+ ));
+}
+
+#[test]
+fn color_word() {
+ assert_json_snapshot!(complete(
+ r#"
+%! main.tex
+\color{re}
+ |
+ ^^"#
+ ));
+}
+
+#[test]
+fn color_open_brace() {
+ assert_json_snapshot!(complete(
+ r#"
+%! main.tex
+\color{
+ |"#
+ ));
+}
+
+#[test]
+fn component_command_simple() {
+ assert_json_snapshot!(complete(
+ r#"
+%! main.tex
+\
+ |"#
+ ));
+}
+
+#[test]
+fn component_command_simple_before() {
+ assert_eq!(
+ complete(
+ r#"
+%! main.tex
+\
+|"#,
+ ),
+ Vec::new()
+ );
+}
+
+#[test]
+fn component_command_simple_package() {
+ assert_json_snapshot!(complete(
+ r#"
+%! main.tex
+\usepackage{lipsum}
+\lips
+ |
+ ^^^^"#
+ ));
+}
+
+#[test]
+fn component_command_bibtex() {
+ assert_json_snapshot!(complete(
+ r#"
+%! main.bib
+@article{b,
+ c = {\LaT }
+ |
+ ^^^
+}"#
+ ));
+}
+
+#[test]
+fn component_environment_simple() {
+ assert_json_snapshot!(complete(
+ r#"
+%! main.tex
+\begin{doc
+ |
+ ^^^"#
+ ));
+}
+
+#[test]
+fn component_environment_simple_end() {
+ assert_json_snapshot!(complete(
+ r#"
+%! main.tex
+\begin{document}
+\end{
+ |"#
+ ));
+}
+
+#[test]
+fn component_environment_class() {
+ assert_json_snapshot!(complete(
+ r#"
+%! main.tex
+\documentclass{article}
+\begin{thein}
+ |
+ ^^^^^"#
+ ));
+}
+
+#[test]
+fn component_environment_command_definition() {
+ assert_json_snapshot!(complete(
+ r#"
+%! main.tex
+\newcommand{\foo}{\begin{doc}
+ |
+ ^^^"#
+ ));
+}
+
+#[test]
+fn entry_type_at_empty() {
+ assert_json_snapshot!(complete(
+ r#"
+%! main.bib
+@
+ |"#
+ ));
+}
+
+#[test]
+fn entry_type_before_preamble() {
+ assert_json_snapshot!(complete(
+ r#"
+%! main.bib
+@preamble
+ |
+ ^^^^^^^^"#
+ ));
+}
+
+#[test]
+fn entry_type_before_string() {
+ assert_json_snapshot!(complete(
+ r#"
+%! main.bib
+@string
+ |
+ ^^^^^^"#
+ ));
+}
+
+#[test]
+fn entry_type_before_article() {
+ assert_json_snapshot!(complete(
+ r#"
+%! main.bib
+@article
+ |
+ ^^^^^^^"#
+ ));
+}
+
+#[test]
+fn entry_type_after_preamble() {
+ assert_json_snapshot!(complete(
+ r#"
+%! main.bib
+@preamble{
+ |
+ ^^^^^^^^"#
+ ));
+}
+
+#[test]
+fn entry_type_after_string() {
+ assert_json_snapshot!(complete(
+ r#"
+%! main.bib
+@string{
+ |
+ ^^^^^^"#
+ ));
+}
+
+#[test]
+fn entry_type_complete_entry() {
+ assert_json_snapshot!(complete(
+ r#"
+%! main.bib
+@article{foo, author = {foo}}
+ |
+ ^^^^^^^"#
+ ));
+}
+
+#[test]
+fn field_empty_entry_open() {
+ assert_json_snapshot!(complete(
+ r#"
+%! main.bib
+@article{foo,
+ |"#
+ ));
+}
+
+#[test]
+fn field_empty_entry_closed() {
+ assert_json_snapshot!(complete(
+ r#"
+%! main.bib
+@article{foo,}
+ |"#
+ ));
+}
+
+#[test]
+fn field_entry_field_name() {
+ assert_json_snapshot!(complete(
+ r#"
+%! main.bib
+@article{foo, a
+ |
+ ^"#
+ ));
+}
+
+#[test]
+fn field_entry_two_fields_name_open() {
+ assert_json_snapshot!(complete(
+ r#"
+%! main.bib
+@article{foo, author = bar, edit
+ |
+ ^^^^"#
+ ));
+}
+
+#[test]
+fn field_entry_two_fields_name_closed() {
+ assert_json_snapshot!(complete(
+ r#"
+%! main.bib
+@article{foo, author = bar, edit}
+ |
+ ^^^^"#
+ ));
+}
+
+#[test]
+fn import_package_open_brace() {
+ assert_json_snapshot!(complete(
+ r#"
+%! main.tex
+\usepackage{lips
+ |
+ ^^^^"#
+ ));
+}
+
+#[test]
+fn import_package_closed_brace() {
+ assert_json_snapshot!(complete(
+ r#"
+%! main.tex
+\usepackage{lips}
+ |
+ ^^^^"#
+ ));
+}
+
+#[test]
+fn import_class_open_brace() {
+ assert_json_snapshot!(complete(
+ r#"
+%! main.tex
+\documentclass{art \foo
+ |
+ ^^^"#
+ ));
+}
+
+#[test]
+fn import_class_closed_brace() {
+ assert_json_snapshot!(complete(
+ r#"
+%! main.tex
+\documentclass{art}
+ |
+ ^^^"#
+ ));
+}
+
+#[test]
+fn label() {
+ assert_json_snapshot!(complete(
+ r#"
+%! foo.tex
+\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}
+
+%! bar.tex
+\section{Bar}%
+\label{sec:bar}
+
+Lorem ipsum dolor sit amet.
+\ref{}
+ |
+
+%! foo.aux
+\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}"#
+ ));
+}
+
+#[test]
+fn theorem_begin() {
+ assert_json_snapshot!(complete(
+ r#"
+%! main.tex
+\newtheorem{lemma}{Lemma}
+\begin{lem
+ |
+ ^^^"#
+ ));
+}
+
+#[test]
+fn theorem_end() {
+ assert_json_snapshot!(complete(
+ r#"
+%! main.tex
+\newtheorem{lemma}{Lemma}
+\begin{}
+\end{lem
+ |
+ ^^^"#
+ ));
+}
+
+#[test]
+fn tikz_library_open_brace() {
+ assert_json_snapshot!(complete(
+ r#"
+%! main.tex
+\usepgflibrary{
+ |"#
+ ));
+}
+
+#[test]
+fn tikz_library_closed_brace() {
+ assert_json_snapshot!(complete(
+ r#"
+%! main.tex
+\usepgflibrary{}
+ |"#
+ ));
+}
+
+#[test]
+fn test_user_command() {
+ assert_json_snapshot!(complete(
+ r#"
+%! main.tex
+\foobar
+\fooba
+ |
+ ^^^^^
+\begin{foo}
+\end{foo}
+\begin{fo}"#
+ ));
+}
+
+#[test]
+fn test_user_environment() {
+ assert_json_snapshot!(complete(
+ r#"
+%! main.tex
+\foobar
+\fooba
+\begin{foo}
+\end{foo}
+\begin{fo}
+ |
+ ^^"#
+ ));
+}
+
+#[test]
+fn test_project_resolution_import() {
+ assert_json_snapshot!(complete(
+ r#"
+%! main.tex
+\documentclass{article}
+\import{sub}{sub/sub.tex}
+\lipsu
+ |
+ ^^^^^
+
+%! sub/sub.tex
+\input{child.tex}
+
+%! sub/child.tex
+\usepackage{lipsum}"#
+ ));
+}
+
+#[test]
+fn test_project_resolution_texlabroot() {
+ assert_json_snapshot!(complete(
+ r#"
+%! src/main.tex
+\documentclass{article}
+\include{src/foo}
+\lipsu
+ |
+ ^^^^^
+
+%! src/foo.tex
+\include{src/bar}
+
+%! src/bar.tex
+\usepackage{lipsum}
+
+%! .texlabroot"#
+ ));
+}
diff --git a/support/texlab/tests/lsp/text_document/definition.rs b/support/texlab/tests/lsp/text_document/definition.rs
new file mode 100644
index 0000000000..78eed633c8
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/definition.rs
@@ -0,0 +1,156 @@
+use itertools::Itertools;
+use lsp_types::{
+ request::GotoDefinition, ClientCapabilities, GotoDefinitionParams, GotoDefinitionResponse,
+ LocationLink,
+};
+
+use crate::fixture::TestBed;
+
+fn check(fixture: &str) {
+ let test_bed = TestBed::new(fixture).unwrap();
+ test_bed.initialize(ClientCapabilities::default()).unwrap();
+
+ let text_document_position_params = test_bed.cursor().unwrap();
+ let cursor = text_document_position_params.position;
+
+ let origin_selection = test_bed
+ .locations()
+ .iter()
+ .filter(|location| location.uri == text_document_position_params.text_document.uri)
+ .find(|location| cursor >= location.range.start && cursor <= location.range.end);
+
+ let mut expected_links: Vec<_> = test_bed
+ .locations()
+ .iter()
+ .filter(|location| Some(*location) != origin_selection)
+ .batching(|it| {
+ let target_selection_range = it.next()?.range;
+ let target = it.next()?;
+ Some(LocationLink {
+ origin_selection_range: origin_selection.map(|sel| sel.range),
+ target_uri: target.uri.clone(),
+ target_range: target.range,
+ target_selection_range,
+ })
+ })
+ .collect();
+
+ let mut actual_links = match test_bed
+ .client()
+ .send_request::<GotoDefinition>(GotoDefinitionParams {
+ text_document_position_params,
+ partial_result_params: Default::default(),
+ work_done_progress_params: Default::default(),
+ })
+ .unwrap()
+ {
+ Some(GotoDefinitionResponse::Link(links)) => links,
+ Some(GotoDefinitionResponse::Array(_)) => unreachable!(),
+ Some(GotoDefinitionResponse::Scalar(_)) => unreachable!(),
+ None => Vec::new(),
+ };
+
+ sort_links(&mut actual_links);
+ sort_links(&mut expected_links);
+ assert_eq!(actual_links, expected_links);
+}
+
+fn sort_links(links: &mut Vec<LocationLink>) {
+ links.sort_by(|a, b| {
+ let left = (&a.target_uri, a.target_range.start);
+ let right = (&b.target_uri, b.target_range.start);
+ left.cmp(&right)
+ });
+}
+
+#[test]
+fn command_definition() {
+ check(
+ r#"
+%! main.tex
+\DeclareMathOperator{\foo}{foo}
+ ^^^^
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+\foo
+ |
+^^^^"#,
+ )
+}
+
+#[test]
+fn document() {
+ check(
+ r#"
+%! foo.tex
+\addbibresource{baz.bib}
+ |
+ ^^^^^^^
+
+%! bar.bib
+@article{foo, bar = {baz}}
+
+%! baz.bib
+@article{foo, bar = {baz}}
+!
+!"#,
+ )
+}
+
+#[test]
+fn entry() {
+ check(
+ r#"
+%! foo.tex
+\addbibresource{baz.bib}
+\cite{foo}
+ |
+ ^^^
+
+%! bar.bib
+@article{foo, bar = {baz}}
+
+%! baz.bib
+@article{foo, bar = {baz}}
+ ^^^
+^^^^^^^^^^^^^^^^^^^^^^^^^^"#,
+ )
+}
+
+#[test]
+fn string_simple() {
+ check(
+ r#"
+%! main.bib
+@string{foo = {bar}}
+ ^^^
+^^^^^^^^^^^^^^^^^^^^
+@article{bar, author = foo}
+ |
+ ^^^"#,
+ )
+}
+
+#[test]
+fn string_join() {
+ check(
+ r#"
+%! main.bib
+@string{foo = {bar}}
+ ^^^
+^^^^^^^^^^^^^^^^^^^^
+@article{bar, author = foo # "bar"}
+ |
+ ^^^"#,
+ )
+}
+
+#[test]
+fn string_field() {
+ check(
+ r#"
+%! main.bib
+@string{foo = {bar}}
+@article{bar, author = foo # "bar"}
+ |"#,
+ )
+}
diff --git a/support/texlab/tests/lsp/text_document/document_highlight.rs b/support/texlab/tests/lsp/text_document/document_highlight.rs
new file mode 100644
index 0000000000..5cfb8fbee8
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/document_highlight.rs
@@ -0,0 +1,51 @@
+use assert_unordered::assert_eq_unordered;
+use lsp_types::{
+ request::DocumentHighlightRequest, ClientCapabilities, DocumentHighlight,
+ DocumentHighlightKind, DocumentHighlightParams,
+};
+
+use crate::fixture::TestBed;
+
+fn check(fixture: &str, highlight_kinds: &[DocumentHighlightKind]) {
+ let test_bed = TestBed::new(fixture).unwrap();
+ test_bed.initialize(ClientCapabilities::default()).unwrap();
+
+ let expected: Vec<_> = test_bed
+ .locations()
+ .iter()
+ .zip(highlight_kinds)
+ .map(|(location, kind)| DocumentHighlight {
+ range: location.range,
+ kind: Some(*kind),
+ })
+ .collect();
+
+ let text_document_position_params = test_bed.cursor().unwrap();
+ let actual = test_bed
+ .client()
+ .send_request::<DocumentHighlightRequest>(DocumentHighlightParams {
+ text_document_position_params,
+ partial_result_params: Default::default(),
+ work_done_progress_params: Default::default(),
+ })
+ .unwrap()
+ .unwrap_or_default();
+
+ assert_eq_unordered!(actual, expected);
+}
+
+#[test]
+fn test_label() {
+ check(
+ r#"
+%! main.tex
+\label{foo}
+ |
+ ^^^
+\ref{foo}
+ ^^^
+\label{bar}
+"#,
+ &[DocumentHighlightKind::WRITE, DocumentHighlightKind::READ],
+ )
+}
diff --git a/support/texlab/tests/lsp/text_document/document_link.rs b/support/texlab/tests/lsp/text_document/document_link.rs
new file mode 100644
index 0000000000..6aab6105de
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/document_link.rs
@@ -0,0 +1,52 @@
+use insta::assert_json_snapshot;
+use lsp_types::{
+ request::DocumentLinkRequest, ClientCapabilities, DocumentLink, DocumentLinkParams,
+};
+
+use crate::fixture::TestBed;
+
+fn find_links(fixture: &str) -> Vec<DocumentLink> {
+ let test_bed = TestBed::new(fixture).unwrap();
+ test_bed.initialize(ClientCapabilities::default()).unwrap();
+
+ let text_document_position = test_bed.cursor().unwrap();
+ let mut links = test_bed
+ .client()
+ .send_request::<DocumentLinkRequest>(DocumentLinkParams {
+ text_document: text_document_position.text_document,
+ work_done_progress_params: Default::default(),
+ partial_result_params: Default::default(),
+ })
+ .unwrap()
+ .unwrap_or_default();
+
+ for link in &mut links {
+ link.target = Some(test_bed.redact(link.target.as_ref().unwrap()));
+ }
+
+ links
+}
+
+#[test]
+fn document_include() {
+ assert_json_snapshot!(find_links(
+ r#"
+%! foo.tex
+\input{bar.tex}
+|
+
+%! bar.tex"#,
+ ))
+}
+
+#[test]
+fn document_import() {
+ assert_json_snapshot!(find_links(
+ r#"
+%! foo.tex
+\import{.}{bar/baz}
+|
+
+%! bar/baz.tex"#,
+ ))
+}
diff --git a/support/texlab/tests/lsp/text_document/document_symbol.rs b/support/texlab/tests/lsp/text_document/document_symbol.rs
new file mode 100644
index 0000000000..6ac00176b9
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/document_symbol.rs
@@ -0,0 +1,441 @@
+use insta::assert_json_snapshot;
+use lsp_types::{
+ notification::DidChangeConfiguration, request::DocumentSymbolRequest,
+ DidChangeConfigurationParams, DocumentSymbolParams, DocumentSymbolResponse,
+};
+
+use crate::fixture::TestBed;
+
+fn find_symbols(
+ fixture: &str,
+ capabilities: serde_json::Value,
+ settings: serde_json::Value,
+) -> DocumentSymbolResponse {
+ let test_bed = TestBed::new(fixture).unwrap();
+
+ test_bed
+ .initialize(serde_json::from_value(capabilities).unwrap())
+ .unwrap();
+
+ test_bed
+ .client()
+ .send_notification::<DidChangeConfiguration>(DidChangeConfigurationParams { settings })
+ .unwrap();
+
+ let text_document = test_bed.cursor().unwrap().text_document;
+
+ let mut response = test_bed
+ .client()
+ .send_request::<DocumentSymbolRequest>(DocumentSymbolParams {
+ text_document,
+ work_done_progress_params: Default::default(),
+ partial_result_params: Default::default(),
+ })
+ .unwrap()
+ .unwrap_or_else(|| DocumentSymbolResponse::Flat(vec![]));
+
+ if let DocumentSymbolResponse::Flat(symbols) = &mut response {
+ for symbol in symbols {
+ symbol.location.uri = test_bed.redact(&symbol.location.uri);
+ }
+ }
+
+ response
+}
+
+#[test]
+fn enumerate_nested() {
+ assert_json_snapshot!(find_symbols(
+ r#"
+%! main.tex
+\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}
+|
+
+%! main.aux
+\relax
+\newlabel{it:foo}{{1}{1}}
+\newlabel{it:qux}{{2}{1}}"#,
+ serde_json::json!({
+ "textDocument": {
+ "documentSymbol": {
+ "hierarchicalDocumentSymbolSupport": true,
+ },
+ },
+ }),
+ serde_json::Value::Null,
+ ));
+}
+
+#[test]
+fn enumerate_flat() {
+ assert_json_snapshot!(find_symbols(
+ r#"
+%! main.tex
+\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}
+|
+
+%! main.aux
+\relax
+\newlabel{it:foo}{{1}{1}}
+\newlabel{it:qux}{{2}{1}}"#,
+ serde_json::json!({}),
+ serde_json::Value::Null,
+ ));
+}
+
+#[test]
+fn equation_nested() {
+ assert_json_snapshot!(find_symbols(
+ r#"
+%! main.tex
+\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}
+|
+
+%! main.aux
+\relax
+\newlabel{eq:foo}{{1}{1}}"#,
+ serde_json::json!({
+ "textDocument": {
+ "documentSymbol": {
+ "hierarchicalDocumentSymbolSupport": true,
+ },
+ },
+ }),
+ serde_json::Value::Null,
+ ));
+}
+
+#[test]
+fn equation_flat() {
+ assert_json_snapshot!(find_symbols(
+ r#"
+%! main.tex
+\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}
+|
+
+%! main.aux
+\relax
+\newlabel{eq:foo}{{1}{1}}"#,
+ serde_json::json!({}),
+ serde_json::Value::Null,
+ ));
+}
+
+#[test]
+fn float_nested() {
+ assert_json_snapshot!(find_symbols(
+ r#"
+%! main.tex
+\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}
+|
+
+%! main.aux
+\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 }"#,
+ serde_json::json!({
+ "textDocument": {
+ "documentSymbol": {
+ "hierarchicalDocumentSymbolSupport": true,
+ },
+ },
+ }),
+ serde_json::Value::Null,
+ ));
+}
+
+#[test]
+fn float_flat() {
+ assert_json_snapshot!(find_symbols(
+ r#"
+%! main.tex
+\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}
+|
+
+%! main.aux
+\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 }"#,
+ serde_json::json!({}),
+ serde_json::Value::Null,
+ ));
+}
+
+#[test]
+fn section_nested() {
+ assert_json_snapshot!(find_symbols(
+ r#"
+%! main.tex
+\documentclass{article}
+
+\begin{document}
+
+\section{Foo}
+
+\section{Bar}\label{sec:bar}
+
+\subsection{Baz}\label{sec:baz}
+
+\end{document}
+|
+
+%! main.aux
+\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}}"#,
+ serde_json::json!({
+ "textDocument": {
+ "documentSymbol": {
+ "hierarchicalDocumentSymbolSupport": true,
+ },
+ },
+ }),
+ serde_json::Value::Null,
+ ));
+}
+
+#[test]
+fn section_flat() {
+ assert_json_snapshot!(find_symbols(
+ r#"
+%! main.tex
+\documentclass{article}
+
+\begin{document}
+
+\section{Foo}
+
+\section{Bar}\label{sec:bar}
+
+\subsection{Baz}\label{sec:baz}
+
+\end{document}
+|
+
+%! main.aux
+\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}}"#,
+ serde_json::json!({}),
+ serde_json::Value::Null,
+ ));
+}
+
+#[test]
+fn theorem_nested() {
+ assert_json_snapshot!(find_symbols(
+ r#"
+%! main.tex
+\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}
+|
+
+%! main.aux
+\relax
+\newlabel{thm:foo}{{1}{1}}
+\newlabel{thm:bar}{{2}{1}}"#,
+ serde_json::json!({
+ "textDocument": {
+ "documentSymbol": {
+ "hierarchicalDocumentSymbolSupport": true,
+ },
+ },
+ }),
+ serde_json::Value::Null,
+ ));
+}
+
+#[test]
+fn theorem_flat() {
+ assert_json_snapshot!(find_symbols(
+ r#"
+%! main.tex
+\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}
+|
+
+%! main.aux
+\relax
+\newlabel{thm:foo}{{1}{1}}
+\newlabel{thm:bar}{{2}{1}}"#,
+ serde_json::json!({}),
+ serde_json::Value::Null,
+ ));
+}
+
+#[test]
+fn ignored_patterns() {
+ assert_json_snapshot!(find_symbols(
+ r#"
+%! main.tex
+\documentclass{article}
+
+\begin{document}
+
+\begin{equation}\label{eq:foo}
+ Foo
+\end{equation}
+
+\begin{enumerate}
+ \item Foo
+ \item Bar
+\end{enumerate}
+
+\end{document}
+|"#,
+ serde_json::json!({}),
+ serde_json::json!({
+ "symbols": {
+ "ignoredPatterns": ["Item", "Enumerate"]
+ }
+ }),
+ ));
+}
diff --git a/support/texlab/tests/lsp/text_document/folding_range.rs b/support/texlab/tests/lsp/text_document/folding_range.rs
new file mode 100644
index 0000000000..063ca7533d
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/folding_range.rs
@@ -0,0 +1,66 @@
+use insta::assert_json_snapshot;
+use lsp_types::{
+ request::FoldingRangeRequest, ClientCapabilities, FoldingRange, FoldingRangeParams,
+};
+
+use crate::fixture::TestBed;
+
+fn find_foldings(fixture: &str) -> Vec<FoldingRange> {
+ let test_bed = TestBed::new(fixture).unwrap();
+
+ test_bed.initialize(ClientCapabilities::default()).unwrap();
+
+ let text_document = test_bed.cursor().unwrap().text_document;
+ test_bed
+ .client()
+ .send_request::<FoldingRangeRequest>(FoldingRangeParams {
+ text_document,
+ work_done_progress_params: Default::default(),
+ partial_result_params: Default::default(),
+ })
+ .unwrap()
+ .unwrap_or_default()
+}
+
+#[test]
+fn latex() {
+ assert_json_snapshot!(find_foldings(
+ r#"
+%! main.tex
+\begin{document}
+ \section{Foo}
+ foo
+ \subsection{Bar}
+ bar
+ \section{Baz}
+ baz
+ \section{Qux}
+\end{document}
+|"#
+ ));
+}
+
+#[test]
+fn bibtex() {
+ assert_json_snapshot!(find_foldings(
+ r#"
+%! main.bib
+some junk
+here
+
+@article{foo,
+ author = {bar},
+ title = {baz}
+}
+
+@string{foo = "bar"}
+
+@comment{foo,
+ author = {bar},
+ title = {baz}
+}
+
+@preamble{"foo"}
+|"#
+ ));
+}
diff --git a/support/texlab/tests/lsp/text_document/formatting.rs b/support/texlab/tests/lsp/text_document/formatting.rs
new file mode 100644
index 0000000000..295022c76a
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/formatting.rs
@@ -0,0 +1,134 @@
+use insta::assert_snapshot;
+use lsp_types::{
+ request::Formatting, ClientCapabilities, DocumentFormattingParams, FormattingOptions,
+};
+use texlab::util::{line_index::LineIndex, line_index_ext::LineIndexExt};
+
+use crate::fixture::TestBed;
+
+fn format(fixture: &str) -> String {
+ let test_bed = TestBed::new(fixture).unwrap();
+ test_bed.initialize(ClientCapabilities::default()).unwrap();
+
+ let text_document = test_bed.cursor().unwrap().text_document;
+ let edits = test_bed
+ .client()
+ .send_request::<Formatting>(DocumentFormattingParams {
+ text_document,
+ work_done_progress_params: Default::default(),
+ options: FormattingOptions {
+ insert_spaces: true,
+ tab_size: 4,
+ ..Default::default()
+ },
+ })
+ .unwrap()
+ .unwrap_or_default();
+
+ let old_text = &test_bed.documents()[0].text;
+ let line_index = LineIndex::new(old_text);
+ let mut new_text = String::from(old_text);
+ for edit in edits.into_iter().rev() {
+ let range = line_index.offset_lsp_range(edit.range);
+ new_text.replace_range::<std::ops::Range<usize>>(range.into(), &edit.new_text);
+ }
+
+ new_text
+}
+
+#[test]
+fn bibtex_internal_wrap_long_lines() {
+ assert_snapshot!(format(
+ r#"
+%! main.bib
+@article{foo, bar = {Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+Lorem ipsum dolor sit amet,
+consectetur adipiscing elit. Lorem ipsum dolor sit amet, consectetur adipiscing elit.},}
+|"#,
+ ));
+}
+
+#[test]
+fn bibtex_internal_multiple_entries() {
+ assert_snapshot!(format(
+ r#"
+%! main.bib
+@article{foo, bar = {Lorem ipsum dolor sit amet, consectetur adipiscing elit. Lorem ipsum dolor sit amet,
+consectetur adipiscing elit. Lorem ipsum dolor sit amet, consectetur adipiscing elit.},}
+
+@article{foo, bar = {Lorem ipsum dolor sit amet, consectetur adipiscing elit. Lorem ipsum dolor sit amet,
+consectetur adipiscing elit. Lorem ipsum dolor sit amet, consectetur adipiscing elit.},}"
+|"#,
+ ));
+}
+
+#[test]
+fn bibtex_internal_trailing_comma() {
+ assert_snapshot!(format(
+ r#"
+%! main.bib
+@article{foo, bar = baz}
+|"#,
+ ));
+}
+
+#[test]
+fn bibtex_internal_insert_braces() {
+ assert_snapshot!(format(
+ r#"
+%! main.bib
+@article{foo, bar = baz,
+|"#,
+ ));
+}
+
+#[test]
+fn bibtex_internal_command() {
+ assert_snapshot!(format(
+ r#"
+%! main.bib
+@article{foo, bar = "\baz",}
+|"#,
+ ));
+}
+
+#[test]
+fn bibtex_internal_join_strings() {
+ assert_snapshot!(format(
+ r#"
+%! main.bib
+@article{foo, bar = "baz" # "qux"}
+|"#,
+ ));
+}
+
+#[test]
+fn bibtex_internal_parens() {
+ assert_snapshot!(format(
+ r#"
+%! main.bib
+@article(foo,)
+|"#,
+ ));
+}
+
+#[test]
+fn bibtex_internal_string() {
+ assert_snapshot!(format(
+ r#"
+%! main.bib
+@string{foo="bar"}
+|"#,
+ ));
+}
+
+#[test]
+fn bibtex_internal_preamble() {
+ assert_snapshot!(format(
+ r#"
+%! main.bib
+@preamble{
+ "foo bar baz" }
+|"#,
+ ));
+}
diff --git a/support/texlab/tests/lsp/text_document/hover.rs b/support/texlab/tests/lsp/text_document/hover.rs
new file mode 100644
index 0000000000..5dafceb781
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/hover.rs
@@ -0,0 +1,246 @@
+use insta::assert_json_snapshot;
+use lsp_types::{request::HoverRequest, ClientCapabilities, HoverContents, HoverParams};
+
+use crate::fixture::TestBed;
+
+fn find_hover(fixture: &str) -> Option<HoverContents> {
+ let test_bed = TestBed::new(fixture).unwrap();
+
+ test_bed.initialize(ClientCapabilities::default()).unwrap();
+
+ let text_document_position_params = test_bed.cursor().unwrap();
+
+ test_bed
+ .client()
+ .send_request::<HoverRequest>(HoverParams {
+ text_document_position_params,
+ work_done_progress_params: Default::default(),
+ })
+ .unwrap()
+ .map(|hover| {
+ assert_eq!(hover.range, Some(test_bed.locations()[0].range));
+ hover.contents
+ })
+}
+
+#[test]
+fn empty_latex_document() {
+ assert_eq!(
+ find_hover(
+ r#"
+%! main.tex
+
+|"#
+ ),
+ None,
+ );
+}
+
+#[test]
+fn empty_bibtex_document() {
+ assert_eq!(
+ find_hover(
+ r#"
+%! main.bib
+
+|"#
+ ),
+ None,
+ );
+}
+
+#[test]
+fn citation_inside_cite() {
+ assert_json_snapshot!(find_hover(
+ r#"
+%! main.bib
+@article{foo, author = {Foo Bar}, title = {Baz Qux}, year = 1337}
+
+%! main.tex
+\addbibresource{main.bib}
+\cite{foo}
+ |
+ ^^^"#
+ ));
+}
+
+#[test]
+fn citation_inside_entry() {
+ assert_json_snapshot!(find_hover(
+ r#"
+%! main.bib
+@article{foo, author = {Foo Bar}, title = {Baz Qux}, year = 1337}
+ |
+ ^^^
+
+%! main.tex
+\addbibresource{main.bib}
+\cite{foo}"#
+ ));
+}
+
+#[test]
+fn component_known_package() {
+ assert_json_snapshot!(find_hover(
+ r#"
+%! main.tex
+\usepackage{amsmath}
+ |
+ ^^^^^^^"#
+ ));
+}
+
+#[test]
+fn component_unknown_class() {
+ assert_eq!(
+ find_hover(
+ r#"
+%! main.tex
+\documentclass{abcdefghijklmnop}
+ |"#
+ ),
+ None,
+ );
+}
+
+#[test]
+fn entry_type_known_type() {
+ assert_json_snapshot!(find_hover(
+ r#"
+%! main.bib
+@article{foo,}
+ |
+^^^^^^^^"#
+ ));
+}
+
+#[test]
+fn entry_type_unknown_field() {
+ assert_eq!(
+ find_hover(
+ r#"
+%! main.bib
+@foo{bar,}
+ |"#
+ ),
+ None,
+ );
+}
+
+#[test]
+fn entry_type_key() {
+ assert_eq!(
+ find_hover(
+ r#"
+%! main.bib
+@foo{bar,}
+ |"#
+ ),
+ None,
+ );
+}
+
+#[test]
+fn field_known() {
+ assert_json_snapshot!(find_hover(
+ r#"
+%! main.bib
+@article{foo, author = bar}
+ |
+ ^^^^^^"#
+ ));
+}
+
+#[test]
+fn field_unknown() {
+ assert_eq!(
+ find_hover(
+ r#"
+%! main.bib
+@article{foo, bar = baz}
+ |"#
+ ),
+ None,
+ );
+}
+
+#[test]
+fn section() {
+ assert_json_snapshot!(find_hover(
+ r#"
+%! main.tex
+\section{Foo}
+\label{sec:foo}
+ |
+ ^^^^^^^"#,
+ ));
+}
+
+#[test]
+fn string_inside_reference() {
+ assert_json_snapshot!(find_hover(
+ r#"
+%! main.bib
+@string{foo = "Foo"}
+@string{bar = "Bar"}
+@article{baz, author = bar}
+ |
+ ^^^"#
+ ));
+}
+
+#[test]
+fn string_inside_field() {
+ assert_eq!(
+ find_hover(
+ r#"
+%! main.bib
+@string{foo = "Foo"}
+@string{bar = "Bar"}
+@article{baz, author = bar}
+ |"#
+ ),
+ None,
+ );
+}
+
+#[test]
+fn label_theorem_child_file() {
+ assert_json_snapshot!(find_hover(
+ r#"
+%! main.tex
+\documentclass{article}
+\newtheorem{lemma}{Lemma}
+\include{child}
+\ref{thm:foo}
+ |
+ ^^^^^^^
+
+%! child.tex
+\begin{lemma}\label{thm:foo}
+ 1 + 1 = 2
+\end{lemma}"#
+ ));
+}
+
+#[test]
+fn label_theorem_child_file_mumber() {
+ assert_json_snapshot!(find_hover(
+ r#"
+%! main.tex
+\documentclass{article}
+\newtheorem{lemma}{Lemma}
+\include{child}
+\ref{thm:foo}
+ |
+ ^^^^^^^
+
+%! child.tex
+\begin{lemma}[Foo]\label{thm:foo}
+ 1 + 1 = 2
+\end{lemma}
+
+%! child.aux
+\newlabel{thm:foo}{{1}{1}{Foo}{lemma.1}{}}"#
+ ));
+}
diff --git a/support/texlab/tests/lsp/text_document/inlay_hint.rs b/support/texlab/tests/lsp/text_document/inlay_hint.rs
new file mode 100644
index 0000000000..d8ae8e304a
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/inlay_hint.rs
@@ -0,0 +1,57 @@
+use insta::assert_json_snapshot;
+use lsp_types::{
+ request::InlayHintRequest, ClientCapabilities, InlayHint, InlayHintParams, Position, Range,
+};
+
+use crate::fixture::TestBed;
+
+fn find_hints(fixture: &str) -> Vec<InlayHint> {
+ let test_bed = TestBed::new(fixture).unwrap();
+ test_bed.initialize(ClientCapabilities::default()).unwrap();
+
+ let cursor = test_bed.cursor().unwrap();
+
+ test_bed
+ .client()
+ .send_request::<InlayHintRequest>(InlayHintParams {
+ text_document: cursor.text_document,
+ range: Range::new(Position::new(0, 0), cursor.position),
+ work_done_progress_params: Default::default(),
+ })
+ .unwrap()
+ .unwrap_or_default()
+}
+
+#[test]
+fn label_definition() {
+ assert_json_snapshot!(find_hints(
+ r#"
+%! main.tex
+\documentclass{article}
+\usepackage{caption}
+\begin{document}
+\section{Foo}\label{sec:foo}
+\section{Bar}\label{sec:bar}
+\subsection{Baz}\label{sec:baz}
+\begin{figure}
+ Test
+ \label{fig:qux}
+ \caption{Qux}
+\end{figure}
+\end{document}
+ |
+
+%! main.aux
+\relax
+\providecommand*\caption@xref[2]{\@setref\relax\@undefined{#1}}
+\newlabel{fig:qux}{{\caption@xref {fig:qux}{ on input line 15}}{1}}
+\@writefile{lof}{\contentsline {figure}{\numberline {1}{\ignorespaces Qux\relax }}{1}{}\protected@file@percent }
+\@writefile{toc}{\contentsline {section}{\numberline {1}Foo}{1}{}\protected@file@percent }
+\newlabel{sec:foo}{{1}{1}}
+\@writefile{toc}{\contentsline {section}{\numberline {2}Bar}{1}{}\protected@file@percent }
+\newlabel{sec:bar}{{2}{1}}
+\@writefile{toc}{\contentsline {subsection}{\numberline {2.1}Baz}{1}{}\protected@file@percent }
+\newlabel{sec:baz}{{2.1}{1}}
+\gdef \@abspage@last{1}"#,
+ ));
+}
diff --git a/support/texlab/tests/lsp/text_document/references.rs b/support/texlab/tests/lsp/text_document/references.rs
new file mode 100644
index 0000000000..9933babbd4
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/references.rs
@@ -0,0 +1,270 @@
+use lsp_types::{
+ request::References, ClientCapabilities, Location, ReferenceContext, ReferenceParams,
+};
+
+use crate::fixture::TestBed;
+
+fn sort(locations: &mut Vec<Location>) {
+ locations.sort_by(|a, b| (&a.uri, a.range.start).cmp(&(&b.uri, b.range.start)));
+}
+
+fn check(fixture: &str, context: ReferenceContext) {
+ let test_bed = TestBed::new(fixture).unwrap();
+
+ test_bed.initialize(ClientCapabilities::default()).unwrap();
+
+ let text_document_position = test_bed.cursor().unwrap();
+
+ let mut expected = test_bed.locations().to_vec();
+
+ let mut actual = test_bed
+ .client()
+ .send_request::<References>(ReferenceParams {
+ text_document_position,
+ context,
+ partial_result_params: Default::default(),
+ work_done_progress_params: Default::default(),
+ })
+ .unwrap()
+ .unwrap_or_default();
+
+ sort(&mut actual);
+ sort(&mut expected);
+ assert_eq!(actual, expected);
+}
+
+#[test]
+fn entry_definition() {
+ check(
+ r#"
+%! foo.bib
+@article{foo,}
+ |
+
+%! bar.tex
+\cite{foo}
+ ^^^
+\addbibresource{foo.bib}
+"#,
+ ReferenceContext {
+ include_declaration: false,
+ },
+ )
+}
+
+#[test]
+fn entry_definition_include_decl() {
+ check(
+ r#"
+%! foo.bib
+@article{foo,}
+ |
+ ^^^
+
+%! bar.tex
+\cite{foo}
+ ^^^
+\addbibresource{foo.bib}
+"#,
+ ReferenceContext {
+ include_declaration: true,
+ },
+ )
+}
+
+#[test]
+fn entry_reference() {
+ check(
+ r#"
+%! foo.bib
+@article{foo,}
+
+%! bar.tex
+\cite{foo}
+ |
+ ^^^
+\addbibresource{foo.bib}
+"#,
+ ReferenceContext {
+ include_declaration: false,
+ },
+ )
+}
+
+#[test]
+fn entry_reference_include_decl() {
+ check(
+ r#"
+%! foo.bib
+@article{foo,}
+ ^^^
+
+%! bar.tex
+\cite{foo}
+ |
+ ^^^
+\addbibresource{foo.bib}
+"#,
+ ReferenceContext {
+ include_declaration: true,
+ },
+ )
+}
+
+#[test]
+fn label_definition() {
+ check(
+ r#"
+%! foo.tex
+\label{foo}
+ |
+
+%! bar.tex
+\ref{foo}
+ ^^^
+\input{foo.tex}
+"#,
+ ReferenceContext {
+ include_declaration: false,
+ },
+ )
+}
+
+#[test]
+fn label_definition_include_decl() {
+ check(
+ r#"
+%! foo.tex
+\label{foo}
+ |
+ ^^^
+
+%! bar.tex
+\ref{foo}
+ ^^^
+\input{foo.tex}
+"#,
+ ReferenceContext {
+ include_declaration: true,
+ },
+ )
+}
+
+#[test]
+fn label_reference() {
+ check(
+ r#"
+%! foo.tex
+\label{foo}
+\input{bar.tex}
+
+%! bar.tex
+\ref{foo}
+ |
+ ^^^
+
+%! baz.tex
+\ref{foo}
+ ^^^
+\input{bar.tex}
+"#,
+ ReferenceContext {
+ include_declaration: false,
+ },
+ )
+}
+
+#[test]
+fn label_reference_include_decl() {
+ check(
+ r#"
+%! foo.tex
+\label{foo}
+ ^^^
+\input{bar.tex}
+
+%! bar.tex
+\ref{foo}
+ |
+ ^^^
+
+%! baz.tex
+\ref{foo}
+ ^^^
+\input{bar.tex}
+"#,
+ ReferenceContext {
+ include_declaration: true,
+ },
+ )
+}
+
+#[test]
+fn string_reference() {
+ check(
+ r#"
+%! main.bib
+@string{foo = {Foo}}
+@string{bar = {Bar}}
+@article{baz, author = foo}
+ |
+ ^^^
+"#,
+ ReferenceContext {
+ include_declaration: false,
+ },
+ )
+}
+
+#[test]
+fn string_reference_include_decl() {
+ check(
+ r#"
+%! main.bib
+@string{foo = {Foo}}
+ ^^^
+@string{bar = {Bar}}
+@article{baz, author = foo}
+ |
+ ^^^
+"#,
+ ReferenceContext {
+ include_declaration: true,
+ },
+ )
+}
+
+#[test]
+fn string_definition() {
+ check(
+ r#"
+%! main.bib
+@string{foo = {Foo}}
+ |
+@string{bar = {Bar}}
+@article{baz, author = foo}
+ ^^^
+"#,
+ ReferenceContext {
+ include_declaration: false,
+ },
+ )
+}
+
+#[test]
+fn string_definition_include_decl() {
+ check(
+ r#"
+%! main.bib
+@string{foo = {Foo}}
+ |
+ ^^^
+@string{bar = {Bar}}
+@article{baz, author = foo}
+ ^^^
+"#,
+ ReferenceContext {
+ include_declaration: true,
+ },
+ )
+}
diff --git a/support/texlab/tests/lsp/text_document/rename.rs b/support/texlab/tests/lsp/text_document/rename.rs
new file mode 100644
index 0000000000..183443809a
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/rename.rs
@@ -0,0 +1,106 @@
+use std::collections::HashMap;
+
+use lsp_types::{request::Rename, ClientCapabilities, RenameParams, TextEdit, Url, WorkspaceEdit};
+
+use crate::fixture::TestBed;
+
+fn check(fixture: &str, new_name: &str) {
+ let test_bed = TestBed::new(fixture).unwrap();
+
+ test_bed.initialize(ClientCapabilities::default()).unwrap();
+
+ let mut expected_changes: HashMap<Url, Vec<TextEdit>> = HashMap::new();
+ for location in test_bed.locations() {
+ expected_changes
+ .entry(location.uri.clone())
+ .or_default()
+ .push(TextEdit::new(location.range, new_name.to_string()));
+ }
+
+ let text_document_position = test_bed.cursor().unwrap();
+ let actual_edit = test_bed
+ .client()
+ .send_request::<Rename>(RenameParams {
+ text_document_position,
+ new_name: new_name.to_string(),
+ work_done_progress_params: Default::default(),
+ })
+ .unwrap()
+ .unwrap_or_default();
+
+ assert_eq!(actual_edit, WorkspaceEdit::new(expected_changes));
+}
+
+#[test]
+fn command() {
+ check(
+ r#"
+%! foo.tex
+\baz
+ |
+ ^^^
+\include{bar.tex}
+
+%! bar.tex
+\baz
+ ^^^
+"#,
+ "qux",
+ )
+}
+
+#[test]
+fn entry() {
+ check(
+ r#"
+%! main.bib
+@article{foo, bar = baz}
+ |
+ ^^^
+
+%! main.tex
+\addbibresource{main.bib}
+\cite{foo}
+ ^^^
+"#,
+ "qux",
+ )
+}
+
+#[test]
+fn citation() {
+ check(
+ r#"
+%! main.bib
+@article{foo, bar = baz}
+ ^^^
+
+%! main.tex
+\addbibresource{main.bib}
+\cite{foo}
+ |
+ ^^^
+"#,
+ "qux",
+ )
+}
+
+#[test]
+fn label() {
+ check(
+ r#"
+%! foo.tex
+\label{foo}\include{bar}
+ |
+ ^^^
+
+%! bar.tex
+\ref{foo}
+ ^^^
+
+%! baz.tex
+\ref{foo}
+"#,
+ "bar",
+ )
+}
diff --git a/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__acronym_package_ref.snap b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__acronym_package_ref.snap
new file mode 100644
index 0000000000..19c84e95fc
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__acronym_package_ref.snap
@@ -0,0 +1,10 @@
+---
+source: tests/lsp/text_document/completion.rs
+expression: "complete(r#\"\n%! main.tex\n\\acrodef{fpsLabel}[FPS]{Frames per Second}\n\\ac{f\n |\n ^\"#)"
+---
+[
+ {
+ "label": "fpsLabel",
+ "preselect": false
+ }
+]
diff --git a/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__acronym_ref_empty.snap b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__acronym_ref_empty.snap
new file mode 100644
index 0000000000..23e5b695ef
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__acronym_ref_empty.snap
@@ -0,0 +1,10 @@
+---
+source: tests/lsp/text_document/completion.rs
+expression: "complete(r#\"\n%! main.tex\n\\newacronym[longplural={Frames per Second}]{fpsLabel}{FPS}{Frame per Second}\n\\acrshort{}\n |\"#)"
+---
+[
+ {
+ "label": "fpsLabel",
+ "preselect": false
+ }
+]
diff --git a/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__acronym_ref_open_brace.snap b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__acronym_ref_open_brace.snap
new file mode 100644
index 0000000000..25171b679c
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__acronym_ref_open_brace.snap
@@ -0,0 +1,10 @@
+---
+source: tests/lsp/text_document/completion.rs
+expression: "complete(r#\"\n%! main.tex\n\\newacronym[longplural={Frames per Second}]{fpsLabel}{FPS}{Frame per Second}\n\\acrshort{f\n |\n ^\"#)"
+---
+[
+ {
+ "label": "fpsLabel",
+ "preselect": false
+ }
+]
diff --git a/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__acronym_ref_simple.snap b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__acronym_ref_simple.snap
new file mode 100644
index 0000000000..1d3f3329c1
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__acronym_ref_simple.snap
@@ -0,0 +1,10 @@
+---
+source: tests/lsp/text_document/completion.rs
+expression: "complete(r#\"\n%! main.tex\n\\newacronym[longplural={Frames per Second}]{fpsLabel}{FPS}{Frame per Second}\n\\acrshort{f}\n |\n ^\"#)"
+---
+[
+ {
+ "label": "fpsLabel",
+ "preselect": false
+ }
+]
diff --git a/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__argument_empty.snap b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__argument_empty.snap
new file mode 100644
index 0000000000..ecc48b5a7f
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__argument_empty.snap
@@ -0,0 +1,26 @@
+---
+source: tests/lsp/text_document/completion.rs
+expression: "complete(r#\"\n%! main.tex\n\\usepackage{amsfonts}\n\\mathbb{}\n |\"#)"
+---
+[
+ {
+ "label": "A",
+ "preselect": false
+ },
+ {
+ "label": "B",
+ "preselect": false
+ },
+ {
+ "label": "C",
+ "preselect": false
+ },
+ {
+ "label": "D",
+ "preselect": false
+ },
+ {
+ "label": "E",
+ "preselect": false
+ }
+]
diff --git a/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__argument_open_brace.snap b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__argument_open_brace.snap
new file mode 100644
index 0000000000..530527a384
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__argument_open_brace.snap
@@ -0,0 +1,26 @@
+---
+source: tests/lsp/text_document/completion.rs
+expression: "complete(r#\"\n%! main.tex\n\\usepackage{amsfonts}\n\\mathbb{\n |\nTest\"#)"
+---
+[
+ {
+ "label": "A",
+ "preselect": false
+ },
+ {
+ "label": "B",
+ "preselect": false
+ },
+ {
+ "label": "C",
+ "preselect": false
+ },
+ {
+ "label": "D",
+ "preselect": false
+ },
+ {
+ "label": "E",
+ "preselect": false
+ }
+]
diff --git a/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__argument_word.snap b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__argument_word.snap
new file mode 100644
index 0000000000..c98249c781
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__argument_word.snap
@@ -0,0 +1,10 @@
+---
+source: tests/lsp/text_document/completion.rs
+expression: "complete(r#\"\n%! main.tex\n\\usepackage{amsfonts}\n\\mathbb{A}\n |\n ^\"#)"
+---
+[
+ {
+ "label": "A",
+ "preselect": false
+ }
+]
diff --git a/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__begin_environment_without_snippet_support.snap b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__begin_environment_without_snippet_support.snap
new file mode 100644
index 0000000000..bb9e30ae55
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__begin_environment_without_snippet_support.snap
@@ -0,0 +1,31 @@
+---
+source: tests/lsp/text_document/completion.rs
+expression: "complete(r#\"\n%! main.tex\n\\beg\n |\n ^^^\"#)"
+---
+[
+ {
+ "label": "AtBeginDocument",
+ "detail": "built-in",
+ "preselect": false
+ },
+ {
+ "label": "AtBeginDvi",
+ "detail": "built-in",
+ "preselect": false
+ },
+ {
+ "label": "begin",
+ "detail": "built-in",
+ "preselect": false
+ },
+ {
+ "label": "begingroup",
+ "detail": "built-in",
+ "preselect": false
+ },
+ {
+ "label": "bigwedge",
+ "detail": "â‹€, built-in",
+ "preselect": false
+ }
+]
diff --git a/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__citation.snap b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__citation.snap
new file mode 100644
index 0000000000..b8755fdef0
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__citation.snap
@@ -0,0 +1,16 @@
+---
+source: tests/lsp/text_document/completion.rs
+expression: "complete(r#\"\n%! main.tex\n\\documentclass{article}\n\\bibliography{main}\n\\begin{document}\n\\cite{\n |\n\\end{document}\n\n%! main.bib\n@article{foo:2019,\n author = {Foo Bar},\n title = {Baz Qux},\n year = {2019},\n}\n\n@article{bar:2005,}\"#)"
+---
+[
+ {
+ "label": "bar:2005",
+ "preselect": false,
+ "filterText": "bar:2005 @article bar:2005"
+ },
+ {
+ "label": "foo:2019",
+ "preselect": false,
+ "filterText": "foo:2019 @article foo:2019 author Foo Bar title Baz Qux year 2019"
+ }
+]
diff --git a/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__citation_acronym.snap b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__citation_acronym.snap
new file mode 100644
index 0000000000..36478c45c8
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__citation_acronym.snap
@@ -0,0 +1,11 @@
+---
+source: tests/lsp/text_document/completion.rs
+expression: "complete(r#\"\n%! main.tex\n\\addbibresource{main.bib}\n\\DeclareAcronym{foo}{cite={}}\n |\n\n%! main.bib\n@article{foo,}\"#)"
+---
+[
+ {
+ "label": "foo",
+ "preselect": false,
+ "filterText": "foo @article foo"
+ }
+]
diff --git a/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__citation_open_brace.snap b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__citation_open_brace.snap
new file mode 100644
index 0000000000..41a1d9ced3
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__citation_open_brace.snap
@@ -0,0 +1,11 @@
+---
+source: tests/lsp/text_document/completion.rs
+expression: "complete(r#\"\n%! main.tex\n\\addbibresource{main.bib}\n\\cite{\n |\n\n%! main.bib\n@article{foo,}\"#)"
+---
+[
+ {
+ "label": "foo",
+ "preselect": false,
+ "filterText": "foo @article foo"
+ }
+]
diff --git a/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__citation_open_brace_multiple.snap b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__citation_open_brace_multiple.snap
new file mode 100644
index 0000000000..8ef509b463
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__citation_open_brace_multiple.snap
@@ -0,0 +1,11 @@
+---
+source: tests/lsp/text_document/completion.rs
+expression: "complete(r#\"\n%! main.tex\n\\addbibresource{main.bib}\n\\cite{foo,a\n |\n ^\n\n%! main.bib\n@article{foo,}\"#)"
+---
+[
+ {
+ "label": "foo",
+ "preselect": false,
+ "filterText": "foo @article foo"
+ }
+]
diff --git a/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__color_model_definition_open_brace.snap b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__color_model_definition_open_brace.snap
new file mode 100644
index 0000000000..421335a4a5
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__color_model_definition_open_brace.snap
@@ -0,0 +1,26 @@
+---
+source: tests/lsp/text_document/completion.rs
+expression: "complete(r#\"\n%! main.tex\n\\definecolor{foo}{\n |\"#)"
+---
+[
+ {
+ "label": "HTML",
+ "preselect": false
+ },
+ {
+ "label": "RGB",
+ "preselect": false
+ },
+ {
+ "label": "cmyk",
+ "preselect": false
+ },
+ {
+ "label": "gray",
+ "preselect": false
+ },
+ {
+ "label": "rgb",
+ "preselect": false
+ }
+]
diff --git a/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__color_model_definition_set_open_brace.snap b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__color_model_definition_set_open_brace.snap
new file mode 100644
index 0000000000..5d7338e48e
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__color_model_definition_set_open_brace.snap
@@ -0,0 +1,26 @@
+---
+source: tests/lsp/text_document/completion.rs
+expression: "complete(r#\"\n%! main.tex\n\\definecolorset{\n |\"#)"
+---
+[
+ {
+ "label": "HTML",
+ "preselect": false
+ },
+ {
+ "label": "RGB",
+ "preselect": false
+ },
+ {
+ "label": "cmyk",
+ "preselect": false
+ },
+ {
+ "label": "gray",
+ "preselect": false
+ },
+ {
+ "label": "rgb",
+ "preselect": false
+ }
+]
diff --git a/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__color_model_definition_set_simple.snap b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__color_model_definition_set_simple.snap
new file mode 100644
index 0000000000..811b47c9ee
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__color_model_definition_set_simple.snap
@@ -0,0 +1,26 @@
+---
+source: tests/lsp/text_document/completion.rs
+expression: "complete(r#\"\n%! main.tex\n\\definecolorset{}\n |\"#)"
+---
+[
+ {
+ "label": "HTML",
+ "preselect": false
+ },
+ {
+ "label": "RGB",
+ "preselect": false
+ },
+ {
+ "label": "cmyk",
+ "preselect": false
+ },
+ {
+ "label": "gray",
+ "preselect": false
+ },
+ {
+ "label": "rgb",
+ "preselect": false
+ }
+]
diff --git a/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__color_model_definition_simple.snap b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__color_model_definition_simple.snap
new file mode 100644
index 0000000000..dbb34f08d9
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__color_model_definition_simple.snap
@@ -0,0 +1,26 @@
+---
+source: tests/lsp/text_document/completion.rs
+expression: "complete(r#\"\n%! main.tex\n\\definecolor{foo}{}\n |\"#)"
+---
+[
+ {
+ "label": "HTML",
+ "preselect": false
+ },
+ {
+ "label": "RGB",
+ "preselect": false
+ },
+ {
+ "label": "cmyk",
+ "preselect": false
+ },
+ {
+ "label": "gray",
+ "preselect": false
+ },
+ {
+ "label": "rgb",
+ "preselect": false
+ }
+]
diff --git a/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__color_open_brace.snap b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__color_open_brace.snap
new file mode 100644
index 0000000000..e30fc4c1b0
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__color_open_brace.snap
@@ -0,0 +1,26 @@
+---
+source: tests/lsp/text_document/completion.rs
+expression: "complete(r#\"\n%! main.tex\n\\color{\n |\"#)"
+---
+[
+ {
+ "label": "Apricot",
+ "preselect": false
+ },
+ {
+ "label": "Aquamarine",
+ "preselect": false
+ },
+ {
+ "label": "Bittersweet",
+ "preselect": false
+ },
+ {
+ "label": "Black",
+ "preselect": false
+ },
+ {
+ "label": "Blue",
+ "preselect": false
+ }
+]
diff --git a/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__color_simple.snap b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__color_simple.snap
new file mode 100644
index 0000000000..5aab0d8e3b
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__color_simple.snap
@@ -0,0 +1,26 @@
+---
+source: tests/lsp/text_document/completion.rs
+expression: "complete(r#\"\n%! main.tex\n\\color{}\n |\"#)"
+---
+[
+ {
+ "label": "Apricot",
+ "preselect": false
+ },
+ {
+ "label": "Aquamarine",
+ "preselect": false
+ },
+ {
+ "label": "Bittersweet",
+ "preselect": false
+ },
+ {
+ "label": "Black",
+ "preselect": false
+ },
+ {
+ "label": "Blue",
+ "preselect": false
+ }
+]
diff --git a/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__color_word.snap b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__color_word.snap
new file mode 100644
index 0000000000..fe803dc6cc
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__color_word.snap
@@ -0,0 +1,26 @@
+---
+source: tests/lsp/text_document/completion.rs
+expression: "complete(r#\"\n%! main.tex\n\\color{re}\n |\n ^^\"#)"
+---
+[
+ {
+ "label": "BrickRed",
+ "preselect": false
+ },
+ {
+ "label": "Red",
+ "preselect": false
+ },
+ {
+ "label": "RedOrange",
+ "preselect": false
+ },
+ {
+ "label": "RedViolet",
+ "preselect": false
+ },
+ {
+ "label": "red",
+ "preselect": false
+ }
+]
diff --git a/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__component_command_bibtex.snap b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__component_command_bibtex.snap
new file mode 100644
index 0000000000..616fb22a6b
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__component_command_bibtex.snap
@@ -0,0 +1,31 @@
+---
+source: tests/lsp/text_document/completion.rs
+expression: "complete(r#\"\n%! main.bib\n@article{b,\n c = {\\LaT }\n |\n ^^^\n}\"#)"
+---
+[
+ {
+ "label": "LaTeX",
+ "detail": "built-in",
+ "preselect": false
+ },
+ {
+ "label": "LaTeXe",
+ "detail": "built-in",
+ "preselect": false
+ },
+ {
+ "label": "LastDeclaredEncoding",
+ "detail": "built-in",
+ "preselect": false
+ },
+ {
+ "label": "last",
+ "detail": "built-in",
+ "preselect": false
+ },
+ {
+ "label": "latexreleaseversion",
+ "detail": "built-in",
+ "preselect": false
+ }
+]
diff --git a/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__component_command_simple.snap b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__component_command_simple.snap
new file mode 100644
index 0000000000..0e40056a6e
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__component_command_simple.snap
@@ -0,0 +1,31 @@
+---
+source: tests/lsp/text_document/completion.rs
+expression: "complete(r#\"\n%! main.tex\n\\\n |\"#)"
+---
+[
+ {
+ "label": "!",
+ "detail": "built-in",
+ "preselect": false
+ },
+ {
+ "label": "\"",
+ "detail": "built-in",
+ "preselect": false
+ },
+ {
+ "label": "#",
+ "detail": "built-in",
+ "preselect": false
+ },
+ {
+ "label": "$",
+ "detail": "$, built-in",
+ "preselect": false
+ },
+ {
+ "label": "%",
+ "detail": "built-in",
+ "preselect": false
+ }
+]
diff --git a/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__component_command_simple_package.snap b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__component_command_simple_package.snap
new file mode 100644
index 0000000000..360229fa7a
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__component_command_simple_package.snap
@@ -0,0 +1,31 @@
+---
+source: tests/lsp/text_document/completion.rs
+expression: "complete(r#\"\n%! main.tex\n\\usepackage{lipsum}\n\\lips\n |\n ^^^^\"#)"
+---
+[
+ {
+ "label": "LipsumPar",
+ "detail": "lipsum.sty",
+ "preselect": false
+ },
+ {
+ "label": "LipsumProtect",
+ "detail": "lipsum.sty",
+ "preselect": false
+ },
+ {
+ "label": "LipsumRestoreAll",
+ "detail": "lipsum.sty",
+ "preselect": false
+ },
+ {
+ "label": "lipsum",
+ "detail": "lipsum.sty",
+ "preselect": false
+ },
+ {
+ "label": "lipsumexp",
+ "detail": "lipsum.sty",
+ "preselect": false
+ }
+]
diff --git a/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__component_environment_class.snap b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__component_environment_class.snap
new file mode 100644
index 0000000000..a2e5e17ac4
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__component_environment_class.snap
@@ -0,0 +1,11 @@
+---
+source: tests/lsp/text_document/completion.rs
+expression: "complete(r#\"\n%! main.tex\n\\documentclass{article}\n\\begin{thein}\n |\n ^^^^^\"#)"
+---
+[
+ {
+ "label": "theindex",
+ "detail": "article.cls",
+ "preselect": false
+ }
+]
diff --git a/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__component_environment_command_definition.snap b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__component_environment_command_definition.snap
new file mode 100644
index 0000000000..28d9c6e82c
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__component_environment_command_definition.snap
@@ -0,0 +1,11 @@
+---
+source: tests/lsp/text_document/completion.rs
+expression: "complete(r#\"\n%! main.tex\n\\newcommand{\\foo}{\\begin{doc}\n |\n ^^^\"#)"
+---
+[
+ {
+ "label": "document",
+ "detail": "built-in",
+ "preselect": false
+ }
+]
diff --git a/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__component_environment_simple.snap b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__component_environment_simple.snap
new file mode 100644
index 0000000000..f892dba5a7
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__component_environment_simple.snap
@@ -0,0 +1,11 @@
+---
+source: tests/lsp/text_document/completion.rs
+expression: "complete(r#\"\n%! main.tex\n\\begin{doc\n |\n ^^^\"#)"
+---
+[
+ {
+ "label": "document",
+ "detail": "built-in",
+ "preselect": false
+ }
+]
diff --git a/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__component_environment_simple_end.snap b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__component_environment_simple_end.snap
new file mode 100644
index 0000000000..95a2e1db04
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__component_environment_simple_end.snap
@@ -0,0 +1,31 @@
+---
+source: tests/lsp/text_document/completion.rs
+expression: "complete(r#\"\n%! main.tex\n\\begin{document}\n\\end{\n |\"#)"
+---
+[
+ {
+ "label": "abstract",
+ "detail": "built-in",
+ "preselect": false
+ },
+ {
+ "label": "array",
+ "detail": "built-in",
+ "preselect": false
+ },
+ {
+ "label": "center",
+ "detail": "built-in",
+ "preselect": false
+ },
+ {
+ "label": "csname",
+ "detail": "built-in",
+ "preselect": false
+ },
+ {
+ "label": "document",
+ "detail": "built-in",
+ "preselect": true
+ }
+]
diff --git a/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__entry_type_after_preamble.snap b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__entry_type_after_preamble.snap
new file mode 100644
index 0000000000..0fd8b00254
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__entry_type_after_preamble.snap
@@ -0,0 +1,10 @@
+---
+source: tests/lsp/text_document/completion.rs
+expression: "complete(r#\"\n%! main.bib\n@preamble{\n |\n ^^^^^^^^\"#)"
+---
+[
+ {
+ "label": "preamble",
+ "preselect": false
+ }
+]
diff --git a/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__entry_type_after_string.snap b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__entry_type_after_string.snap
new file mode 100644
index 0000000000..6844fc1862
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__entry_type_after_string.snap
@@ -0,0 +1,10 @@
+---
+source: tests/lsp/text_document/completion.rs
+expression: "complete(r#\"\n%! main.bib\n@string{\n |\n ^^^^^^\"#)"
+---
+[
+ {
+ "label": "string",
+ "preselect": false
+ }
+]
diff --git a/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__entry_type_at_empty.snap b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__entry_type_at_empty.snap
new file mode 100644
index 0000000000..e74f5fed37
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__entry_type_at_empty.snap
@@ -0,0 +1,26 @@
+---
+source: tests/lsp/text_document/completion.rs
+expression: "complete(r#\"\n%! main.bib\n@\n |\"#)"
+---
+[
+ {
+ "label": "article",
+ "preselect": false
+ },
+ {
+ "label": "artwork",
+ "preselect": false
+ },
+ {
+ "label": "audio",
+ "preselect": false
+ },
+ {
+ "label": "bibnote",
+ "preselect": false
+ },
+ {
+ "label": "book",
+ "preselect": false
+ }
+]
diff --git a/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__entry_type_before_article.snap b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__entry_type_before_article.snap
new file mode 100644
index 0000000000..f9defdee01
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__entry_type_before_article.snap
@@ -0,0 +1,10 @@
+---
+source: tests/lsp/text_document/completion.rs
+expression: "complete(r#\"\n%! main.bib\n@article\n |\n ^^^^^^^\"#)"
+---
+[
+ {
+ "label": "article",
+ "preselect": false
+ }
+]
diff --git a/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__entry_type_before_preamble.snap b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__entry_type_before_preamble.snap
new file mode 100644
index 0000000000..aad03a392b
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__entry_type_before_preamble.snap
@@ -0,0 +1,10 @@
+---
+source: tests/lsp/text_document/completion.rs
+expression: "complete(r#\"\n%! main.bib\n@preamble\n |\n ^^^^^^^^\"#)"
+---
+[
+ {
+ "label": "preamble",
+ "preselect": false
+ }
+]
diff --git a/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__entry_type_before_string.snap b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__entry_type_before_string.snap
new file mode 100644
index 0000000000..e30c60a45c
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__entry_type_before_string.snap
@@ -0,0 +1,10 @@
+---
+source: tests/lsp/text_document/completion.rs
+expression: "complete(r#\"\n%! main.bib\n@string\n |\n ^^^^^^\"#)"
+---
+[
+ {
+ "label": "string",
+ "preselect": false
+ }
+]
diff --git a/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__entry_type_complete_entry.snap b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__entry_type_complete_entry.snap
new file mode 100644
index 0000000000..2724026a26
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__entry_type_complete_entry.snap
@@ -0,0 +1,10 @@
+---
+source: tests/lsp/text_document/completion.rs
+expression: "complete(r#\"\n%! main.bib\n@article{foo, author = {foo}}\n |\n ^^^^^^^\"#)"
+---
+[
+ {
+ "label": "article",
+ "preselect": false
+ }
+]
diff --git a/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__field_empty_entry_closed.snap b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__field_empty_entry_closed.snap
new file mode 100644
index 0000000000..a5a1d7838e
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__field_empty_entry_closed.snap
@@ -0,0 +1,26 @@
+---
+source: tests/lsp/text_document/completion.rs
+expression: "complete(r#\"\n%! main.bib\n@article{foo,}\n |\"#)"
+---
+[
+ {
+ "label": "abstract",
+ "preselect": false
+ },
+ {
+ "label": "addendum",
+ "preselect": false
+ },
+ {
+ "label": "address",
+ "preselect": false
+ },
+ {
+ "label": "afterword",
+ "preselect": false
+ },
+ {
+ "label": "annotation",
+ "preselect": false
+ }
+]
diff --git a/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__field_empty_entry_open.snap b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__field_empty_entry_open.snap
new file mode 100644
index 0000000000..693d4ed141
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__field_empty_entry_open.snap
@@ -0,0 +1,26 @@
+---
+source: tests/lsp/text_document/completion.rs
+expression: "complete(r#\"\n%! main.bib\n@article{foo,\n |\"#)"
+---
+[
+ {
+ "label": "abstract",
+ "preselect": false
+ },
+ {
+ "label": "addendum",
+ "preselect": false
+ },
+ {
+ "label": "address",
+ "preselect": false
+ },
+ {
+ "label": "afterword",
+ "preselect": false
+ },
+ {
+ "label": "annotation",
+ "preselect": false
+ }
+]
diff --git a/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__field_entry_field_name.snap b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__field_entry_field_name.snap
new file mode 100644
index 0000000000..cb0d41ed5d
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__field_entry_field_name.snap
@@ -0,0 +1,26 @@
+---
+source: tests/lsp/text_document/completion.rs
+expression: "complete(r#\"\n%! main.bib\n@article{foo, a\n |\n ^\"#)"
+---
+[
+ {
+ "label": "abstract",
+ "preselect": false
+ },
+ {
+ "label": "addendum",
+ "preselect": false
+ },
+ {
+ "label": "address",
+ "preselect": false
+ },
+ {
+ "label": "afterword",
+ "preselect": false
+ },
+ {
+ "label": "annotation",
+ "preselect": false
+ }
+]
diff --git a/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__field_entry_two_fields_name_closed.snap b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__field_entry_two_fields_name_closed.snap
new file mode 100644
index 0000000000..66dbf62785
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__field_entry_two_fields_name_closed.snap
@@ -0,0 +1,26 @@
+---
+source: tests/lsp/text_document/completion.rs
+expression: "complete(r#\"\n%! main.bib\n@article{foo, author = bar, edit}\n |\n ^^^^\"#)"
+---
+[
+ {
+ "label": "edition",
+ "preselect": false
+ },
+ {
+ "label": "editor",
+ "preselect": false
+ },
+ {
+ "label": "editora",
+ "preselect": false
+ },
+ {
+ "label": "editoratype",
+ "preselect": false
+ },
+ {
+ "label": "editorb",
+ "preselect": false
+ }
+]
diff --git a/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__field_entry_two_fields_name_open.snap b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__field_entry_two_fields_name_open.snap
new file mode 100644
index 0000000000..221f78f335
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__field_entry_two_fields_name_open.snap
@@ -0,0 +1,26 @@
+---
+source: tests/lsp/text_document/completion.rs
+expression: "complete(r#\"\n%! main.bib\n@article{foo, author = bar, edit\n |\n ^^^^\"#)"
+---
+[
+ {
+ "label": "edition",
+ "preselect": false
+ },
+ {
+ "label": "editor",
+ "preselect": false
+ },
+ {
+ "label": "editora",
+ "preselect": false
+ },
+ {
+ "label": "editoratype",
+ "preselect": false
+ },
+ {
+ "label": "editorb",
+ "preselect": false
+ }
+]
diff --git a/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__glossary_ref_open_brace.snap b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__glossary_ref_open_brace.snap
new file mode 100644
index 0000000000..1be95d5d99
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__glossary_ref_open_brace.snap
@@ -0,0 +1,10 @@
+---
+source: tests/lsp/text_document/completion.rs
+expression: "complete(r#\"\n%! main.tex\n\\newacronym[longplural={Frames per Second}]{fpsLabel}{FPS}{Frame per Second}\n\\gls{f\n |\n ^\"#)"
+---
+[
+ {
+ "label": "fpsLabel",
+ "preselect": false
+ }
+]
diff --git a/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__glossary_ref_simple.snap b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__glossary_ref_simple.snap
new file mode 100644
index 0000000000..81e9299d69
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__glossary_ref_simple.snap
@@ -0,0 +1,10 @@
+---
+source: tests/lsp/text_document/completion.rs
+expression: "complete(r#\"\n%! main.tex\n\\newacronym[longplural={Frames per Second}]{fpsLabel}{FPS}{Frame per Second}\n\\gls{f}\n |\n ^\"#)"
+---
+[
+ {
+ "label": "fpsLabel",
+ "preselect": false
+ }
+]
diff --git a/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__import_class_closed_brace.snap b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__import_class_closed_brace.snap
new file mode 100644
index 0000000000..9ed7978612
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__import_class_closed_brace.snap
@@ -0,0 +1,26 @@
+---
+source: tests/lsp/text_document/completion.rs
+expression: "complete(r#\"\n%! main.tex\n\\documentclass{art}\n |\n ^^^\"#)"
+---
+[
+ {
+ "label": "article",
+ "preselect": false
+ },
+ {
+ "label": "articleingud",
+ "preselect": false
+ },
+ {
+ "label": "articoletteracdp",
+ "preselect": false
+ },
+ {
+ "label": "artikel1",
+ "preselect": false
+ },
+ {
+ "label": "artikel2",
+ "preselect": false
+ }
+]
diff --git a/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__import_class_open_brace.snap b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__import_class_open_brace.snap
new file mode 100644
index 0000000000..b650b621c6
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__import_class_open_brace.snap
@@ -0,0 +1,26 @@
+---
+source: tests/lsp/text_document/completion.rs
+expression: "complete(r#\"\n%! main.tex\n\\documentclass{art \\foo\n |\n ^^^\"#)"
+---
+[
+ {
+ "label": "article",
+ "preselect": false
+ },
+ {
+ "label": "articleingud",
+ "preselect": false
+ },
+ {
+ "label": "articoletteracdp",
+ "preselect": false
+ },
+ {
+ "label": "artikel1",
+ "preselect": false
+ },
+ {
+ "label": "artikel2",
+ "preselect": false
+ }
+]
diff --git a/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__import_package_closed_brace.snap b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__import_package_closed_brace.snap
new file mode 100644
index 0000000000..f8cad5526c
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__import_package_closed_brace.snap
@@ -0,0 +1,26 @@
+---
+source: tests/lsp/text_document/completion.rs
+expression: "complete(r#\"\n%! main.tex\n\\usepackage{lips}\n |\n ^^^^\"#)"
+---
+[
+ {
+ "label": "lips",
+ "preselect": false
+ },
+ {
+ "label": "lipsum",
+ "preselect": false
+ },
+ {
+ "label": "lisp-simple-alloc",
+ "preselect": false
+ },
+ {
+ "label": "lisp-string",
+ "preselect": false
+ },
+ {
+ "label": "lwarp-lips",
+ "preselect": false
+ }
+]
diff --git a/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__import_package_open_brace.snap b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__import_package_open_brace.snap
new file mode 100644
index 0000000000..d234241e24
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__import_package_open_brace.snap
@@ -0,0 +1,26 @@
+---
+source: tests/lsp/text_document/completion.rs
+expression: "complete(r#\"\n%! main.tex\n\\usepackage{lips\n |\n ^^^^\"#)"
+---
+[
+ {
+ "label": "lips",
+ "preselect": false
+ },
+ {
+ "label": "lipsum",
+ "preselect": false
+ },
+ {
+ "label": "lisp-simple-alloc",
+ "preselect": false
+ },
+ {
+ "label": "lisp-string",
+ "preselect": false
+ },
+ {
+ "label": "lwarp-lips",
+ "preselect": false
+ }
+]
diff --git a/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__label.snap b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__label.snap
new file mode 100644
index 0000000000..a186c56920
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__label.snap
@@ -0,0 +1,36 @@
+---
+source: tests/lsp/text_document/completion.rs
+expression: "complete(r#\"\n%! foo.tex\n\\documentclass{article}\n\n\\usepackage{amsmath}\n\\usepackage{caption}\n\\usepackage{amsthm}\n\\newtheorem{lemma}{Lemma}\n\n\\begin{document}\n\n\\section{Foo}%\n\\label{sec:foo}\n\n\\begin{equation}%\n\\label{eq:foo}\n 1 + 1 = 2\n\\end{equation}\n\n\\begin{equation}%\n\\label{eq:bar}\n 1 + 1 = 2\n\\end{equation}\n\n\\begin{figure}%\n\\LaTeX{}\n\\caption{Baz}%\n\\label{fig:baz}\n\\end{figure}\n\n\\begin{lemma}%\n\\label{thm:foo}\n 1 + 1 = 2\n\\end{lemma}\n\n\\include{bar}\n\n\\end{document}\n\n%! bar.tex\n\\section{Bar}%\n\\label{sec:bar}\n\nLorem ipsum dolor sit amet.\n\\ref{}\n |\n\n%! foo.aux\n\\relax\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {1}{\\ignorespaces Baz\\relax }}{1}\\protected@file@percent }\n\\providecommand*\\caption@xref[2]{\\@setref\\relax\\@undefined{#1}}\n\\newlabel{fig:baz}{{1}{1}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {1}Foo}{1}\\protected@file@percent }\n\\newlabel{sec:foo}{{1}{1}}\n\\newlabel{eq:foo}{{1}{1}}\n\\newlabel{eq:bar}{{2}{1}}\n\\newlabel{thm:foo}{{1}{1}}\n\\@input{bar.aux}\"#)"
+---
+[
+ {
+ "label": "eq:bar",
+ "detail": "Equation (2)",
+ "preselect": false,
+ "filterText": "eq:bar Equation (2)"
+ },
+ {
+ "label": "eq:foo",
+ "detail": "Equation (1)",
+ "preselect": false,
+ "filterText": "eq:foo Equation (1)"
+ },
+ {
+ "label": "fig:baz",
+ "detail": "Figure 1",
+ "preselect": false,
+ "filterText": "fig:baz Figure 1: Baz"
+ },
+ {
+ "label": "sec:bar",
+ "detail": "Section (Bar)",
+ "preselect": false,
+ "filterText": "sec:bar Section (Bar)"
+ },
+ {
+ "label": "sec:foo",
+ "detail": "Section 1 (Foo)",
+ "preselect": false,
+ "filterText": "sec:foo Section 1 (Foo)"
+ }
+]
diff --git a/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__project_resolution_import.snap b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__project_resolution_import.snap
new file mode 100644
index 0000000000..351dc96deb
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__project_resolution_import.snap
@@ -0,0 +1,31 @@
+---
+source: tests/lsp/text_document/completion.rs
+expression: "complete(r#\"\n%! main.tex\n\\documentclass{article}\n\\import{sub}{sub/sub.tex}\n\\lipsu\n |\n ^^^^^\n\n%! sub/sub.tex\n\\input{child.tex}\n\n%! sub/child.tex\n\\usepackage{lipsum}\"#)"
+---
+[
+ {
+ "label": "LipsumPar",
+ "detail": "lipsum.sty",
+ "preselect": false
+ },
+ {
+ "label": "LipsumProtect",
+ "detail": "lipsum.sty",
+ "preselect": false
+ },
+ {
+ "label": "LipsumRestoreAll",
+ "detail": "lipsum.sty",
+ "preselect": false
+ },
+ {
+ "label": "lipsum",
+ "detail": "lipsum.sty",
+ "preselect": false
+ },
+ {
+ "label": "lipsumexp",
+ "detail": "lipsum.sty",
+ "preselect": false
+ }
+]
diff --git a/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__project_resolution_texlabroot.snap b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__project_resolution_texlabroot.snap
new file mode 100644
index 0000000000..8ddefbc6ad
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__project_resolution_texlabroot.snap
@@ -0,0 +1,31 @@
+---
+source: tests/lsp/text_document/completion.rs
+expression: "complete(r#\"\n%! src/main.tex\n\\documentclass{article}\n\\include{src/foo}\n\\lipsu\n |\n ^^^^^\n\n%! src/foo.tex\n\\include{src/bar}\n\n%! src/bar.tex\n\\usepackage{lipsum}\n\n%! .texlabroot\"#)"
+---
+[
+ {
+ "label": "LipsumPar",
+ "detail": "lipsum.sty",
+ "preselect": false
+ },
+ {
+ "label": "LipsumProtect",
+ "detail": "lipsum.sty",
+ "preselect": false
+ },
+ {
+ "label": "LipsumRestoreAll",
+ "detail": "lipsum.sty",
+ "preselect": false
+ },
+ {
+ "label": "lipsum",
+ "detail": "lipsum.sty",
+ "preselect": false
+ },
+ {
+ "label": "lipsumexp",
+ "detail": "lipsum.sty",
+ "preselect": false
+ }
+]
diff --git a/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__theorem_begin.snap b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__theorem_begin.snap
new file mode 100644
index 0000000000..f60725667f
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__theorem_begin.snap
@@ -0,0 +1,11 @@
+---
+source: tests/lsp/text_document/completion.rs
+expression: "complete(r#\"\n%! main.tex\n\\newtheorem{lemma}{Lemma}\n\\begin{lem\n |\n ^^^\"#)"
+---
+[
+ {
+ "label": "lemma",
+ "detail": "user-defined",
+ "preselect": false
+ }
+]
diff --git a/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__theorem_end.snap b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__theorem_end.snap
new file mode 100644
index 0000000000..0ec951db1d
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__theorem_end.snap
@@ -0,0 +1,11 @@
+---
+source: tests/lsp/text_document/completion.rs
+expression: "complete(r#\"\n%! main.tex\n\\newtheorem{lemma}{Lemma}\n\\begin{}\n\\end{lem\n |\n ^^^\"#)"
+---
+[
+ {
+ "label": "lemma",
+ "detail": "user-defined",
+ "preselect": false
+ }
+]
diff --git a/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__tikz_library_closed_brace.snap b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__tikz_library_closed_brace.snap
new file mode 100644
index 0000000000..e727fbbc1d
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__tikz_library_closed_brace.snap
@@ -0,0 +1,26 @@
+---
+source: tests/lsp/text_document/completion.rs
+expression: "complete(r#\"\n%! main.tex\n\\usepgflibrary{}\n |\"#)"
+---
+[
+ {
+ "label": "arrows",
+ "preselect": false
+ },
+ {
+ "label": "arrows.meta",
+ "preselect": false
+ },
+ {
+ "label": "arrows.spaced",
+ "preselect": false
+ },
+ {
+ "label": "curvilinear",
+ "preselect": false
+ },
+ {
+ "label": "datavisualization.barcharts",
+ "preselect": false
+ }
+]
diff --git a/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__tikz_library_open_brace.snap b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__tikz_library_open_brace.snap
new file mode 100644
index 0000000000..668b445c91
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__tikz_library_open_brace.snap
@@ -0,0 +1,26 @@
+---
+source: tests/lsp/text_document/completion.rs
+expression: "complete(r#\"\n%! main.tex\n\\usepgflibrary{\n |\"#)"
+---
+[
+ {
+ "label": "arrows",
+ "preselect": false
+ },
+ {
+ "label": "arrows.meta",
+ "preselect": false
+ },
+ {
+ "label": "arrows.spaced",
+ "preselect": false
+ },
+ {
+ "label": "curvilinear",
+ "preselect": false
+ },
+ {
+ "label": "datavisualization.barcharts",
+ "preselect": false
+ }
+]
diff --git a/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__user_command.snap b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__user_command.snap
new file mode 100644
index 0000000000..83e2e701ea
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__user_command.snap
@@ -0,0 +1,11 @@
+---
+source: tests/lsp/text_document/completion.rs
+expression: "complete(r#\"\n%! main.tex\n\\foobar\n\\fooba\n |\n ^^^^^\n\\begin{foo}\n\\end{foo}\n\\begin{fo}\"#)"
+---
+[
+ {
+ "label": "foobar",
+ "detail": "user-defined",
+ "preselect": false
+ }
+]
diff --git a/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__user_environment.snap b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__user_environment.snap
new file mode 100644
index 0000000000..c46a9beb26
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__user_environment.snap
@@ -0,0 +1,16 @@
+---
+source: tests/lsp/text_document/completion.rs
+expression: "complete(r#\"\n%! main.tex\n\\foobar\n\\fooba\n\\begin{foo}\n\\end{foo}\n\\begin{fo}\n |\n ^^\"#)"
+---
+[
+ {
+ "label": "filecontents",
+ "detail": "built-in",
+ "preselect": false
+ },
+ {
+ "label": "foo",
+ "detail": "user-defined",
+ "preselect": false
+ }
+]
diff --git a/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__document_link__document_import.snap b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__document_link__document_import.snap
new file mode 100644
index 0000000000..b27d0f7a32
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__document_link__document_import.snap
@@ -0,0 +1,19 @@
+---
+source: tests/lsp/text_document/document_link.rs
+expression: "find_links(r#\"\n%! foo.tex\n\\import{.}{bar/baz}\n|\n\n%! bar/baz.tex\"#)"
+---
+[
+ {
+ "range": {
+ "start": {
+ "line": 0,
+ "character": 11
+ },
+ "end": {
+ "line": 0,
+ "character": 18
+ }
+ },
+ "target": "file:///bar/baz.tex"
+ }
+]
diff --git a/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__document_link__document_include.snap b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__document_link__document_include.snap
new file mode 100644
index 0000000000..fd88cb4a8c
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__document_link__document_include.snap
@@ -0,0 +1,19 @@
+---
+source: tests/lsp/text_document/document_link.rs
+expression: "find_links(r#\"\n%! foo.tex\n\\input{bar.tex}\n|\n\n%! bar.tex\"#)"
+---
+[
+ {
+ "range": {
+ "start": {
+ "line": 0,
+ "character": 7
+ },
+ "end": {
+ "line": 0,
+ "character": 14
+ }
+ },
+ "target": "file:///bar.tex"
+ }
+]
diff --git a/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__document_symbol__enumerate_flat.snap b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__document_symbol__enumerate_flat.snap
new file mode 100644
index 0000000000..cd18c3f06e
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__document_symbol__enumerate_flat.snap
@@ -0,0 +1,96 @@
+---
+source: tests/lsp/text_document/document_symbol.rs
+expression: "find_symbols(r#\"\n%! main.tex\n\\documentclass{article}\n\n\\begin{document}\n\n\\begin{enumerate}\n \\item\\label{it:foo} Foo\n \\item\\label{it:bar} Bar\n \\item[Baz] Baz\n \\item[Qux]\\label{it:qux} Qux\n\\end{enumerate}\n\n\\end{document}\n|\n\n%! main.aux\n\\relax\n\\newlabel{it:foo}{{1}{1}}\n\\newlabel{it:qux}{{2}{1}}\"#,\n serde_json::json!({}), serde_json::Value::Null)"
+---
+[
+ {
+ "name": "Enumerate",
+ "kind": 10,
+ "deprecated": false,
+ "location": {
+ "uri": "file:///main.tex",
+ "range": {
+ "start": {
+ "line": 4,
+ "character": 0
+ },
+ "end": {
+ "line": 9,
+ "character": 15
+ }
+ }
+ }
+ },
+ {
+ "name": "1",
+ "kind": 22,
+ "deprecated": false,
+ "location": {
+ "uri": "file:///main.tex",
+ "range": {
+ "start": {
+ "line": 5,
+ "character": 4
+ },
+ "end": {
+ "line": 5,
+ "character": 27
+ }
+ }
+ }
+ },
+ {
+ "name": "Item",
+ "kind": 22,
+ "deprecated": false,
+ "location": {
+ "uri": "file:///main.tex",
+ "range": {
+ "start": {
+ "line": 6,
+ "character": 4
+ },
+ "end": {
+ "line": 6,
+ "character": 27
+ }
+ }
+ }
+ },
+ {
+ "name": "Baz",
+ "kind": 22,
+ "deprecated": false,
+ "location": {
+ "uri": "file:///main.tex",
+ "range": {
+ "start": {
+ "line": 7,
+ "character": 4
+ },
+ "end": {
+ "line": 7,
+ "character": 18
+ }
+ }
+ }
+ },
+ {
+ "name": "2",
+ "kind": 22,
+ "deprecated": false,
+ "location": {
+ "uri": "file:///main.tex",
+ "range": {
+ "start": {
+ "line": 8,
+ "character": 4
+ },
+ "end": {
+ "line": 8,
+ "character": 32
+ }
+ }
+ }
+ }
+]
diff --git a/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__document_symbol__enumerate_nested.snap b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__document_symbol__enumerate_nested.snap
new file mode 100644
index 0000000000..8f777b6d8f
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__document_symbol__enumerate_nested.snap
@@ -0,0 +1,140 @@
+---
+source: tests/lsp/text_document/document_symbol.rs
+expression: "find_symbols(r#\"\n%! main.tex\n\\documentclass{article}\n\n\\begin{document}\n\n\\begin{enumerate}\n \\item\\label{it:foo} Foo\n \\item\\label{it:bar} Bar\n \\item[Baz] Baz\n \\item[Qux]\\label{it:qux} Qux\n\\end{enumerate}\n\n\\end{document}\n|\n\n%! main.aux\n\\relax\n\\newlabel{it:foo}{{1}{1}}\n\\newlabel{it:qux}{{2}{1}}\"#,\n serde_json::json!({\n \"textDocument\" :\n {\n \"documentSymbol\" :\n { \"hierarchicalDocumentSymbolSupport\" : true, },\n },\n }), serde_json::Value::Null)"
+---
+[
+ {
+ "name": "Enumerate",
+ "kind": 10,
+ "deprecated": false,
+ "range": {
+ "start": {
+ "line": 4,
+ "character": 0
+ },
+ "end": {
+ "line": 9,
+ "character": 15
+ }
+ },
+ "selectionRange": {
+ "start": {
+ "line": 4,
+ "character": 0
+ },
+ "end": {
+ "line": 9,
+ "character": 15
+ }
+ },
+ "children": [
+ {
+ "name": "1",
+ "detail": "it:foo",
+ "kind": 22,
+ "deprecated": false,
+ "range": {
+ "start": {
+ "line": 5,
+ "character": 4
+ },
+ "end": {
+ "line": 5,
+ "character": 27
+ }
+ },
+ "selectionRange": {
+ "start": {
+ "line": 5,
+ "character": 9
+ },
+ "end": {
+ "line": 5,
+ "character": 23
+ }
+ },
+ "children": []
+ },
+ {
+ "name": "Item",
+ "detail": "it:bar",
+ "kind": 22,
+ "deprecated": false,
+ "range": {
+ "start": {
+ "line": 6,
+ "character": 4
+ },
+ "end": {
+ "line": 6,
+ "character": 27
+ }
+ },
+ "selectionRange": {
+ "start": {
+ "line": 6,
+ "character": 9
+ },
+ "end": {
+ "line": 6,
+ "character": 23
+ }
+ },
+ "children": []
+ },
+ {
+ "name": "Baz",
+ "kind": 22,
+ "deprecated": false,
+ "range": {
+ "start": {
+ "line": 7,
+ "character": 4
+ },
+ "end": {
+ "line": 7,
+ "character": 18
+ }
+ },
+ "selectionRange": {
+ "start": {
+ "line": 7,
+ "character": 4
+ },
+ "end": {
+ "line": 7,
+ "character": 18
+ }
+ },
+ "children": []
+ },
+ {
+ "name": "2",
+ "detail": "it:qux",
+ "kind": 22,
+ "deprecated": false,
+ "range": {
+ "start": {
+ "line": 8,
+ "character": 4
+ },
+ "end": {
+ "line": 8,
+ "character": 32
+ }
+ },
+ "selectionRange": {
+ "start": {
+ "line": 8,
+ "character": 14
+ },
+ "end": {
+ "line": 8,
+ "character": 28
+ }
+ },
+ "children": []
+ }
+ ]
+ }
+]
diff --git a/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__document_symbol__equation_flat.snap b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__document_symbol__equation_flat.snap
new file mode 100644
index 0000000000..d37fe4cbbf
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__document_symbol__equation_flat.snap
@@ -0,0 +1,60 @@
+---
+source: tests/lsp/text_document/document_symbol.rs
+expression: "find_symbols(r#\"\n%! main.tex\n\\documentclass{article}\n\n\\begin{document}\n\n\\begin{equation}\\label{eq:foo}\n Foo\n\\end{equation}\n\n\\begin{equation}\\label{eq:bar}\n Bar\n\\end{equation}\n\n\\begin{equation}\n Baz\n\\end{equation}\n\n\\end{document}\n|\n\n%! main.aux\n\\relax\n\\newlabel{eq:foo}{{1}{1}}\"#,\n serde_json::json!({}), serde_json::Value::Null)"
+---
+[
+ {
+ "name": "Equation (1)",
+ "kind": 14,
+ "deprecated": false,
+ "location": {
+ "uri": "file:///main.tex",
+ "range": {
+ "start": {
+ "line": 4,
+ "character": 0
+ },
+ "end": {
+ "line": 6,
+ "character": 14
+ }
+ }
+ }
+ },
+ {
+ "name": "Equation",
+ "kind": 14,
+ "deprecated": false,
+ "location": {
+ "uri": "file:///main.tex",
+ "range": {
+ "start": {
+ "line": 8,
+ "character": 0
+ },
+ "end": {
+ "line": 10,
+ "character": 14
+ }
+ }
+ }
+ },
+ {
+ "name": "Equation",
+ "kind": 14,
+ "deprecated": false,
+ "location": {
+ "uri": "file:///main.tex",
+ "range": {
+ "start": {
+ "line": 12,
+ "character": 0
+ },
+ "end": {
+ "line": 14,
+ "character": 14
+ }
+ }
+ }
+ }
+]
diff --git a/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__document_symbol__equation_nested.snap b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__document_symbol__equation_nested.snap
new file mode 100644
index 0000000000..5965400814
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__document_symbol__equation_nested.snap
@@ -0,0 +1,86 @@
+---
+source: tests/lsp/text_document/document_symbol.rs
+expression: "find_symbols(r#\"\n%! main.tex\n\\documentclass{article}\n\n\\begin{document}\n\n\\begin{equation}\\label{eq:foo}\n Foo\n\\end{equation}\n\n\\begin{equation}\\label{eq:bar}\n Bar\n\\end{equation}\n\n\\begin{equation}\n Baz\n\\end{equation}\n\n\\end{document}\n|\n\n%! main.aux\n\\relax\n\\newlabel{eq:foo}{{1}{1}}\"#,\n serde_json::json!({\n \"textDocument\" :\n {\n \"documentSymbol\" :\n { \"hierarchicalDocumentSymbolSupport\" : true, },\n },\n }), serde_json::Value::Null)"
+---
+[
+ {
+ "name": "Equation (1)",
+ "detail": "eq:foo",
+ "kind": 14,
+ "deprecated": false,
+ "range": {
+ "start": {
+ "line": 4,
+ "character": 0
+ },
+ "end": {
+ "line": 6,
+ "character": 14
+ }
+ },
+ "selectionRange": {
+ "start": {
+ "line": 4,
+ "character": 16
+ },
+ "end": {
+ "line": 4,
+ "character": 30
+ }
+ },
+ "children": []
+ },
+ {
+ "name": "Equation",
+ "detail": "eq:bar",
+ "kind": 14,
+ "deprecated": false,
+ "range": {
+ "start": {
+ "line": 8,
+ "character": 0
+ },
+ "end": {
+ "line": 10,
+ "character": 14
+ }
+ },
+ "selectionRange": {
+ "start": {
+ "line": 8,
+ "character": 16
+ },
+ "end": {
+ "line": 8,
+ "character": 30
+ }
+ },
+ "children": []
+ },
+ {
+ "name": "Equation",
+ "kind": 14,
+ "deprecated": false,
+ "range": {
+ "start": {
+ "line": 12,
+ "character": 0
+ },
+ "end": {
+ "line": 14,
+ "character": 14
+ }
+ },
+ "selectionRange": {
+ "start": {
+ "line": 12,
+ "character": 0
+ },
+ "end": {
+ "line": 14,
+ "character": 14
+ }
+ },
+ "children": []
+ }
+]
diff --git a/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__document_symbol__float_flat.snap b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__document_symbol__float_flat.snap
new file mode 100644
index 0000000000..e93be78925
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__document_symbol__float_flat.snap
@@ -0,0 +1,60 @@
+---
+source: tests/lsp/text_document/document_symbol.rs
+expression: "find_symbols(r#\"\n%! main.tex\n\\documentclass{article}\n\n\\begin{document}\n\n\\begin{figure}\n Foo\n \\caption{Foo}\\label{fig:foo}\n\\end{figure}\n\n\\begin{figure}\n Bar\n \\caption{Bar}\\label{fig:bar}\n\\end{figure}\n\n\\begin{figure}\n Baz\n \\caption{Baz}\n\\end{figure}\n\n\\begin{figure}\n Qux\n\\end{figure}\n\n\\end{document}\n|\n\n%! main.aux\n\\relax\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {1}{\\ignorespaces Foo}}{1}\\protected@file@percent }\n\\newlabel{fig:foo}{{1}{1}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {2}{\\ignorespaces Bar}}{1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {3}{\\ignorespaces Baz}}{1}\\protected@file@percent }\"#,\n serde_json::json!({}), serde_json::Value::Null)"
+---
+[
+ {
+ "name": "Figure 1: Foo",
+ "kind": 6,
+ "deprecated": false,
+ "location": {
+ "uri": "file:///main.tex",
+ "range": {
+ "start": {
+ "line": 4,
+ "character": 0
+ },
+ "end": {
+ "line": 7,
+ "character": 12
+ }
+ }
+ }
+ },
+ {
+ "name": "Figure: Bar",
+ "kind": 6,
+ "deprecated": false,
+ "location": {
+ "uri": "file:///main.tex",
+ "range": {
+ "start": {
+ "line": 9,
+ "character": 0
+ },
+ "end": {
+ "line": 12,
+ "character": 12
+ }
+ }
+ }
+ },
+ {
+ "name": "Figure: Baz",
+ "kind": 6,
+ "deprecated": false,
+ "location": {
+ "uri": "file:///main.tex",
+ "range": {
+ "start": {
+ "line": 14,
+ "character": 0
+ },
+ "end": {
+ "line": 17,
+ "character": 12
+ }
+ }
+ }
+ }
+]
diff --git a/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__document_symbol__float_nested.snap b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__document_symbol__float_nested.snap
new file mode 100644
index 0000000000..a625beb1fb
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__document_symbol__float_nested.snap
@@ -0,0 +1,86 @@
+---
+source: tests/lsp/text_document/document_symbol.rs
+expression: "find_symbols(r#\"\n%! main.tex\n\\documentclass{article}\n\n\\begin{document}\n\n\\begin{figure}\n Foo\n \\caption{Foo}\\label{fig:foo}\n\\end{figure}\n\n\\begin{figure}\n Bar\n \\caption{Bar}\\label{fig:bar}\n\\end{figure}\n\n\\begin{figure}\n Baz\n \\caption{Baz}\n\\end{figure}\n\n\\begin{figure}\n Qux\n\\end{figure}\n\n\\end{document}\n|\n\n%! main.aux\n\\relax\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {1}{\\ignorespaces Foo}}{1}\\protected@file@percent }\n\\newlabel{fig:foo}{{1}{1}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {2}{\\ignorespaces Bar}}{1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {3}{\\ignorespaces Baz}}{1}\\protected@file@percent }\"#,\n serde_json::json!({\n \"textDocument\" :\n {\n \"documentSymbol\" :\n { \"hierarchicalDocumentSymbolSupport\" : true, },\n },\n }), serde_json::Value::Null)"
+---
+[
+ {
+ "name": "Figure 1: Foo",
+ "detail": "fig:foo",
+ "kind": 6,
+ "deprecated": false,
+ "range": {
+ "start": {
+ "line": 4,
+ "character": 0
+ },
+ "end": {
+ "line": 7,
+ "character": 12
+ }
+ },
+ "selectionRange": {
+ "start": {
+ "line": 6,
+ "character": 17
+ },
+ "end": {
+ "line": 6,
+ "character": 32
+ }
+ },
+ "children": []
+ },
+ {
+ "name": "Figure: Bar",
+ "detail": "fig:bar",
+ "kind": 6,
+ "deprecated": false,
+ "range": {
+ "start": {
+ "line": 9,
+ "character": 0
+ },
+ "end": {
+ "line": 12,
+ "character": 12
+ }
+ },
+ "selectionRange": {
+ "start": {
+ "line": 11,
+ "character": 17
+ },
+ "end": {
+ "line": 11,
+ "character": 32
+ }
+ },
+ "children": []
+ },
+ {
+ "name": "Figure: Baz",
+ "kind": 6,
+ "deprecated": false,
+ "range": {
+ "start": {
+ "line": 14,
+ "character": 0
+ },
+ "end": {
+ "line": 17,
+ "character": 12
+ }
+ },
+ "selectionRange": {
+ "start": {
+ "line": 14,
+ "character": 0
+ },
+ "end": {
+ "line": 17,
+ "character": 12
+ }
+ },
+ "children": []
+ }
+]
diff --git a/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__document_symbol__ignored_patterns.snap b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__document_symbol__ignored_patterns.snap
new file mode 100644
index 0000000000..90c445fc01
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__document_symbol__ignored_patterns.snap
@@ -0,0 +1,24 @@
+---
+source: tests/lsp/text_document/document_symbol.rs
+expression: "find_symbols(r#\"\n%! main.tex\n\\documentclass{article}\n\n\\begin{document}\n\n\\begin{equation}\\label{eq:foo}\n Foo\n\\end{equation}\n \n\\begin{enumerate}\n \\item Foo\n \\item Bar\n\\end{enumerate}\n\n\\end{document}\n|\"#,\n serde_json::json!({}),\n serde_json::json!({\n \"symbols\" : { \"ignoredPatterns\" : [\"Item\", \"Enumerate\"] }\n }))"
+---
+[
+ {
+ "name": "Equation",
+ "kind": 14,
+ "deprecated": false,
+ "location": {
+ "uri": "file:///main.tex",
+ "range": {
+ "start": {
+ "line": 4,
+ "character": 0
+ },
+ "end": {
+ "line": 6,
+ "character": 14
+ }
+ }
+ }
+ }
+]
diff --git a/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__document_symbol__section_flat.snap b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__document_symbol__section_flat.snap
new file mode 100644
index 0000000000..85d9cef9e8
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__document_symbol__section_flat.snap
@@ -0,0 +1,60 @@
+---
+source: tests/lsp/text_document/document_symbol.rs
+expression: "find_symbols(r#\"\n%! main.tex\n\\documentclass{article}\n\n\\begin{document}\n\n\\section{Foo}\n\n\\section{Bar}\\label{sec:bar}\n\n\\subsection{Baz}\\label{sec:baz}\n\n\\end{document}\n|\n\n%! main.aux\n\\relax\n\\@writefile{toc}{\\contentsline {section}{\\numberline {1}Foo}{1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {2}Bar}{1}\\protected@file@percent }\n\\newlabel{sec:bar}{{2}{1}}\"#,\n serde_json::json!({}), serde_json::Value::Null)"
+---
+[
+ {
+ "name": "Foo",
+ "kind": 2,
+ "deprecated": false,
+ "location": {
+ "uri": "file:///main.tex",
+ "range": {
+ "start": {
+ "line": 4,
+ "character": 0
+ },
+ "end": {
+ "line": 4,
+ "character": 13
+ }
+ }
+ }
+ },
+ {
+ "name": "2 Bar",
+ "kind": 2,
+ "deprecated": false,
+ "location": {
+ "uri": "file:///main.tex",
+ "range": {
+ "start": {
+ "line": 6,
+ "character": 0
+ },
+ "end": {
+ "line": 8,
+ "character": 31
+ }
+ }
+ }
+ },
+ {
+ "name": "Baz",
+ "kind": 2,
+ "deprecated": false,
+ "location": {
+ "uri": "file:///main.tex",
+ "range": {
+ "start": {
+ "line": 8,
+ "character": 0
+ },
+ "end": {
+ "line": 8,
+ "character": 31
+ }
+ }
+ }
+ }
+]
diff --git a/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__document_symbol__section_nested.snap b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__document_symbol__section_nested.snap
new file mode 100644
index 0000000000..bac292bf01
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__document_symbol__section_nested.snap
@@ -0,0 +1,87 @@
+---
+source: tests/lsp/text_document/document_symbol.rs
+expression: "find_symbols(r#\"\n%! main.tex\n\\documentclass{article}\n\n\\begin{document}\n\n\\section{Foo}\n\n\\section{Bar}\\label{sec:bar}\n\n\\subsection{Baz}\\label{sec:baz}\n\n\\end{document}\n|\n\n%! main.aux\n\\relax\n\\@writefile{toc}{\\contentsline {section}{\\numberline {1}Foo}{1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {2}Bar}{1}\\protected@file@percent }\n\\newlabel{sec:bar}{{2}{1}}\"#,\n serde_json::json!({\n \"textDocument\" :\n {\n \"documentSymbol\" :\n { \"hierarchicalDocumentSymbolSupport\" : true, },\n },\n }), serde_json::Value::Null)"
+---
+[
+ {
+ "name": "Foo",
+ "kind": 2,
+ "deprecated": false,
+ "range": {
+ "start": {
+ "line": 4,
+ "character": 0
+ },
+ "end": {
+ "line": 4,
+ "character": 13
+ }
+ },
+ "selectionRange": {
+ "start": {
+ "line": 4,
+ "character": 0
+ },
+ "end": {
+ "line": 4,
+ "character": 13
+ }
+ },
+ "children": []
+ },
+ {
+ "name": "2 Bar",
+ "detail": "sec:bar",
+ "kind": 2,
+ "deprecated": false,
+ "range": {
+ "start": {
+ "line": 6,
+ "character": 0
+ },
+ "end": {
+ "line": 8,
+ "character": 31
+ }
+ },
+ "selectionRange": {
+ "start": {
+ "line": 6,
+ "character": 13
+ },
+ "end": {
+ "line": 6,
+ "character": 28
+ }
+ },
+ "children": [
+ {
+ "name": "Baz",
+ "detail": "sec:baz",
+ "kind": 2,
+ "deprecated": false,
+ "range": {
+ "start": {
+ "line": 8,
+ "character": 0
+ },
+ "end": {
+ "line": 8,
+ "character": 31
+ }
+ },
+ "selectionRange": {
+ "start": {
+ "line": 8,
+ "character": 16
+ },
+ "end": {
+ "line": 8,
+ "character": 31
+ }
+ },
+ "children": []
+ }
+ ]
+ }
+]
diff --git a/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__document_symbol__theorem_flat.snap b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__document_symbol__theorem_flat.snap
new file mode 100644
index 0000000000..55ab44980c
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__document_symbol__theorem_flat.snap
@@ -0,0 +1,78 @@
+---
+source: tests/lsp/text_document/document_symbol.rs
+expression: "find_symbols(r#\"\n%! main.tex\n\\documentclass{article}\n\\usepackage{amsthm}\n\\newtheorem{lemma}{Lemma}\n\n\\begin{document}\n\n\\begin{lemma}[Foo]\\label{thm:foo}\n Foo\n\\end{lemma}\n\n\\begin{lemma}\\label{thm:bar}\n Bar\n\\end{lemma}\n\n\\begin{lemma}\\label{thm:baz}\n Baz\n\\end{lemma}\n\n\\begin{lemma}[Qux]\n Qux\n\\end{lemma}\n\n\\end{document}\n|\n\n%! main.aux\n\\relax\n\\newlabel{thm:foo}{{1}{1}}\n\\newlabel{thm:bar}{{2}{1}}\"#,\n serde_json::json!({}), serde_json::Value::Null)"
+---
+[
+ {
+ "name": "Lemma 1 (Foo)",
+ "kind": 13,
+ "deprecated": false,
+ "location": {
+ "uri": "file:///main.tex",
+ "range": {
+ "start": {
+ "line": 6,
+ "character": 0
+ },
+ "end": {
+ "line": 8,
+ "character": 11
+ }
+ }
+ }
+ },
+ {
+ "name": "Lemma 2",
+ "kind": 13,
+ "deprecated": false,
+ "location": {
+ "uri": "file:///main.tex",
+ "range": {
+ "start": {
+ "line": 10,
+ "character": 0
+ },
+ "end": {
+ "line": 12,
+ "character": 11
+ }
+ }
+ }
+ },
+ {
+ "name": "Lemma",
+ "kind": 13,
+ "deprecated": false,
+ "location": {
+ "uri": "file:///main.tex",
+ "range": {
+ "start": {
+ "line": 14,
+ "character": 0
+ },
+ "end": {
+ "line": 16,
+ "character": 11
+ }
+ }
+ }
+ },
+ {
+ "name": "Lemma (Qux)",
+ "kind": 13,
+ "deprecated": false,
+ "location": {
+ "uri": "file:///main.tex",
+ "range": {
+ "start": {
+ "line": 18,
+ "character": 0
+ },
+ "end": {
+ "line": 20,
+ "character": 11
+ }
+ }
+ }
+ }
+]
diff --git a/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__document_symbol__theorem_nested.snap b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__document_symbol__theorem_nested.snap
new file mode 100644
index 0000000000..82bdd6d1d9
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__document_symbol__theorem_nested.snap
@@ -0,0 +1,113 @@
+---
+source: tests/lsp/text_document/document_symbol.rs
+expression: "find_symbols(r#\"\n%! main.tex\n\\documentclass{article}\n\\usepackage{amsthm}\n\\newtheorem{lemma}{Lemma}\n\n\\begin{document}\n\n\\begin{lemma}[Foo]\\label{thm:foo}\n Foo\n\\end{lemma}\n\n\\begin{lemma}\\label{thm:bar}\n Bar\n\\end{lemma}\n\n\\begin{lemma}\\label{thm:baz}\n Baz\n\\end{lemma}\n\n\\begin{lemma}[Qux]\n Qux\n\\end{lemma}\n\n\\end{document}\n|\n\n%! main.aux\n\\relax\n\\newlabel{thm:foo}{{1}{1}}\n\\newlabel{thm:bar}{{2}{1}}\"#,\n serde_json::json!({\n \"textDocument\" :\n {\n \"documentSymbol\" :\n { \"hierarchicalDocumentSymbolSupport\" : true, },\n },\n }), serde_json::Value::Null)"
+---
+[
+ {
+ "name": "Lemma 1 (Foo)",
+ "detail": "thm:foo",
+ "kind": 13,
+ "deprecated": false,
+ "range": {
+ "start": {
+ "line": 6,
+ "character": 0
+ },
+ "end": {
+ "line": 8,
+ "character": 11
+ }
+ },
+ "selectionRange": {
+ "start": {
+ "line": 6,
+ "character": 18
+ },
+ "end": {
+ "line": 6,
+ "character": 33
+ }
+ },
+ "children": []
+ },
+ {
+ "name": "Lemma 2",
+ "detail": "thm:bar",
+ "kind": 13,
+ "deprecated": false,
+ "range": {
+ "start": {
+ "line": 10,
+ "character": 0
+ },
+ "end": {
+ "line": 12,
+ "character": 11
+ }
+ },
+ "selectionRange": {
+ "start": {
+ "line": 10,
+ "character": 13
+ },
+ "end": {
+ "line": 10,
+ "character": 28
+ }
+ },
+ "children": []
+ },
+ {
+ "name": "Lemma",
+ "detail": "thm:baz",
+ "kind": 13,
+ "deprecated": false,
+ "range": {
+ "start": {
+ "line": 14,
+ "character": 0
+ },
+ "end": {
+ "line": 16,
+ "character": 11
+ }
+ },
+ "selectionRange": {
+ "start": {
+ "line": 14,
+ "character": 13
+ },
+ "end": {
+ "line": 14,
+ "character": 28
+ }
+ },
+ "children": []
+ },
+ {
+ "name": "Lemma (Qux)",
+ "kind": 13,
+ "deprecated": false,
+ "range": {
+ "start": {
+ "line": 18,
+ "character": 0
+ },
+ "end": {
+ "line": 20,
+ "character": 11
+ }
+ },
+ "selectionRange": {
+ "start": {
+ "line": 18,
+ "character": 0
+ },
+ "end": {
+ "line": 20,
+ "character": 11
+ }
+ },
+ "children": []
+ }
+]
diff --git a/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__folding_range__bibtex.snap b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__folding_range__bibtex.snap
new file mode 100644
index 0000000000..2063cc39e5
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__folding_range__bibtex.snap
@@ -0,0 +1,27 @@
+---
+source: tests/lsp/text_document/folding_range.rs
+expression: "find_foldings(r#\"\n%! main.bib\nsome junk\nhere\n\n@article{foo,\n author = {bar},\n title = {baz}\n}\n\n@string{foo = \"bar\"}\n\n@comment{foo,\n author = {bar},\n title = {baz}\n}\n\n@preamble{\"foo\"}\n|\"#)"
+---
+[
+ {
+ "startLine": 3,
+ "startCharacter": 0,
+ "endLine": 6,
+ "endCharacter": 1,
+ "kind": "region"
+ },
+ {
+ "startLine": 8,
+ "startCharacter": 0,
+ "endLine": 8,
+ "endCharacter": 20,
+ "kind": "region"
+ },
+ {
+ "startLine": 15,
+ "startCharacter": 0,
+ "endLine": 15,
+ "endCharacter": 16,
+ "kind": "region"
+ }
+]
diff --git a/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__folding_range__latex.snap b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__folding_range__latex.snap
new file mode 100644
index 0000000000..74632eda56
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__folding_range__latex.snap
@@ -0,0 +1,41 @@
+---
+source: tests/lsp/text_document/folding_range.rs
+expression: "find_foldings(r#\"\n%! main.tex\n\\begin{document}\n \\section{Foo}\n foo\n \\subsection{Bar}\n bar\n \\section{Baz}\n baz\n \\section{Qux}\n\\end{document}\n|\"#)"
+---
+[
+ {
+ "startLine": 0,
+ "startCharacter": 0,
+ "endLine": 8,
+ "endCharacter": 14,
+ "kind": "region"
+ },
+ {
+ "startLine": 1,
+ "startCharacter": 4,
+ "endLine": 4,
+ "endCharacter": 7,
+ "kind": "region"
+ },
+ {
+ "startLine": 3,
+ "startCharacter": 4,
+ "endLine": 4,
+ "endCharacter": 7,
+ "kind": "region"
+ },
+ {
+ "startLine": 5,
+ "startCharacter": 4,
+ "endLine": 6,
+ "endCharacter": 7,
+ "kind": "region"
+ },
+ {
+ "startLine": 7,
+ "startCharacter": 4,
+ "endLine": 7,
+ "endCharacter": 17,
+ "kind": "region"
+ }
+]
diff --git a/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__formatting__bibtex_internal_command.snap b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__formatting__bibtex_internal_command.snap
new file mode 100644
index 0000000000..b98dd68758
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__formatting__bibtex_internal_command.snap
@@ -0,0 +1,7 @@
+---
+source: tests/lsp/text_document/formatting.rs
+expression: "format(r#\"\n%! main.bib\n@article{foo, bar = \"\\baz\",}\n|\"#)"
+---
+@article{foo,
+ bar = "\baz",
+}
diff --git a/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__formatting__bibtex_internal_insert_braces.snap b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__formatting__bibtex_internal_insert_braces.snap
new file mode 100644
index 0000000000..a95f5d3006
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__formatting__bibtex_internal_insert_braces.snap
@@ -0,0 +1,7 @@
+---
+source: tests/lsp/text_document/formatting.rs
+expression: "format(r#\"\n%! main.bib\n@article{foo, bar = baz,\n|\"#)"
+---
+@article{foo,
+ bar = baz,
+}
diff --git a/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__formatting__bibtex_internal_join_strings.snap b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__formatting__bibtex_internal_join_strings.snap
new file mode 100644
index 0000000000..3ea6bf18ad
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__formatting__bibtex_internal_join_strings.snap
@@ -0,0 +1,7 @@
+---
+source: tests/lsp/text_document/formatting.rs
+expression: "format(r#\"\n%! main.bib\n@article{foo, bar = \"baz\" # \"qux\"}\n|\"#)"
+---
+@article{foo,
+ bar = "baz" # "qux",
+}
diff --git a/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__formatting__bibtex_internal_multiple_entries.snap b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__formatting__bibtex_internal_multiple_entries.snap
new file mode 100644
index 0000000000..966f133a76
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__formatting__bibtex_internal_multiple_entries.snap
@@ -0,0 +1,15 @@
+---
+source: tests/lsp/text_document/formatting.rs
+expression: "format(r#\"\n%! main.bib\n@article{foo, bar = {Lorem ipsum dolor sit amet, consectetur adipiscing elit. Lorem ipsum dolor sit amet, \nconsectetur adipiscing elit. Lorem ipsum dolor sit amet, consectetur adipiscing elit.},}\n\n@article{foo, bar = {Lorem ipsum dolor sit amet, consectetur adipiscing elit. Lorem ipsum dolor sit amet, \nconsectetur adipiscing elit. Lorem ipsum dolor sit amet, consectetur adipiscing elit.},}\"\n|\"#)"
+---
+@article{foo,
+ bar = {Lorem ipsum dolor sit amet, consectetur adipiscing elit. Lorem ipsum
+ dolor sit amet, consectetur adipiscing elit. Lorem ipsum dolor sit
+ amet, consectetur adipiscing elit.},
+}
+
+@article{foo,
+ bar = {Lorem ipsum dolor sit amet, consectetur adipiscing elit. Lorem ipsum
+ dolor sit amet, consectetur adipiscing elit. Lorem ipsum dolor sit
+ amet, consectetur adipiscing elit.},
+}"
diff --git a/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__formatting__bibtex_internal_parens.snap b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__formatting__bibtex_internal_parens.snap
new file mode 100644
index 0000000000..ca317db108
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__formatting__bibtex_internal_parens.snap
@@ -0,0 +1,6 @@
+---
+source: tests/lsp/text_document/formatting.rs
+expression: "format(r#\"\n%! main.bib\n@article(foo,)\n|\"#)"
+---
+@article{foo,
+}
diff --git a/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__formatting__bibtex_internal_preamble.snap b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__formatting__bibtex_internal_preamble.snap
new file mode 100644
index 0000000000..6448e3a791
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__formatting__bibtex_internal_preamble.snap
@@ -0,0 +1,5 @@
+---
+source: tests/lsp/text_document/formatting.rs
+expression: "format(r#\"\n%! main.bib\n@preamble{\n \"foo bar baz\" }\n|\"#)"
+---
+@preamble{"foo bar baz"}
diff --git a/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__formatting__bibtex_internal_string.snap b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__formatting__bibtex_internal_string.snap
new file mode 100644
index 0000000000..a1739290fd
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__formatting__bibtex_internal_string.snap
@@ -0,0 +1,5 @@
+---
+source: tests/lsp/text_document/formatting.rs
+expression: "format(r#\"\n%! main.bib\n@string{foo=\"bar\"}\n|\"#)"
+---
+@string{foo = "bar"}
diff --git a/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__formatting__bibtex_internal_trailing_comma.snap b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__formatting__bibtex_internal_trailing_comma.snap
new file mode 100644
index 0000000000..dd5c7b49f0
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__formatting__bibtex_internal_trailing_comma.snap
@@ -0,0 +1,7 @@
+---
+source: tests/lsp/text_document/formatting.rs
+expression: "format(r#\"\n%! main.bib\n@article{foo, bar = baz}\n|\"#)"
+---
+@article{foo,
+ bar = baz,
+}
diff --git a/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__formatting__bibtex_internal_wrap_long_lines.snap b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__formatting__bibtex_internal_wrap_long_lines.snap
new file mode 100644
index 0000000000..c52b8a9317
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__formatting__bibtex_internal_wrap_long_lines.snap
@@ -0,0 +1,9 @@
+---
+source: tests/lsp/text_document/formatting.rs
+expression: "format(r#\"\n%! main.bib\n@article{foo, bar = {Lorem ipsum dolor sit amet, consectetur adipiscing elit.\nLorem ipsum dolor sit amet,\nconsectetur adipiscing elit. Lorem ipsum dolor sit amet, consectetur adipiscing elit.},}\n|\"#)"
+---
+@article{foo,
+ bar = {Lorem ipsum dolor sit amet, consectetur adipiscing elit. Lorem ipsum
+ dolor sit amet, consectetur adipiscing elit. Lorem ipsum dolor sit
+ amet, consectetur adipiscing elit.},
+}
diff --git a/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__hover__citation_inside_cite.snap b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__hover__citation_inside_cite.snap
new file mode 100644
index 0000000000..6e6c6b264c
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__hover__citation_inside_cite.snap
@@ -0,0 +1,8 @@
+---
+source: tests/lsp/text_document/hover.rs
+expression: "find_hover(r#\"\n%! main.bib\n@article{foo, author = {Foo Bar}, title = {Baz Qux}, year = 1337}\n\n%! main.tex\n\\addbibresource{main.bib}\n\\cite{foo}\n |\n ^^^\"#)"
+---
+{
+ "kind": "markdown",
+ "value": "F. Bar: \"Baz Qux\". (1337)."
+}
diff --git a/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__hover__citation_inside_entry.snap b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__hover__citation_inside_entry.snap
new file mode 100644
index 0000000000..c13c16051d
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__hover__citation_inside_entry.snap
@@ -0,0 +1,8 @@
+---
+source: tests/lsp/text_document/hover.rs
+expression: "find_hover(r#\"\n%! main.bib\n@article{foo, author = {Foo Bar}, title = {Baz Qux}, year = 1337}\n |\n ^^^\n\n%! main.tex\n\\addbibresource{main.bib}\n\\cite{foo}\"#)"
+---
+{
+ "kind": "markdown",
+ "value": "F. Bar: \"Baz Qux\". (1337)."
+}
diff --git a/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__hover__component_known_package.snap b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__hover__component_known_package.snap
new file mode 100644
index 0000000000..ea1801d4ed
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__hover__component_known_package.snap
@@ -0,0 +1,8 @@
+---
+source: tests/lsp/text_document/hover.rs
+expression: "find_hover(r#\"\n%! main.tex\n\\usepackage{amsmath}\n |\n ^^^^^^^\"#)"
+---
+{
+ "kind": "plaintext",
+ "value": "The package provides the principal packages in the AMS-LaTeX distribution. It adapts for use in LaTeX most of the mathematical features found in AMS-TeX; it is highly recommended as an adjunct to serious mathematical typesetting in LaTeX. When amsmath is loaded, AMS-LaTeX packages amsbsy (for bold symbols), amsopn (for operator names) and amstext (for text embedded in mathematics) are also loaded. amsmath is part of the LaTeX required distribution; however, several contributed packages add still further to its appeal; examples are empheq, which provides functions for decorating and highlighting mathematics, and ntheorem, for specifying theorem (and similar) definitions."
+}
diff --git a/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__hover__entry_type_known_type.snap b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__hover__entry_type_known_type.snap
new file mode 100644
index 0000000000..f3b146fb21
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__hover__entry_type_known_type.snap
@@ -0,0 +1,8 @@
+---
+source: tests/lsp/text_document/hover.rs
+expression: "find_hover(r#\"\n%! main.bib\n@article{foo,}\n |\n^^^^^^^^\"#)"
+---
+{
+ "kind": "markdown",
+ "value": "An article in a journal, magazine, newspaper, or other periodical which forms a \n self-contained unit with its own title. The title of the periodical is given in the \n journaltitle field. If the issue has its own title in addition to the main title of \n the periodical, it goes in the issuetitle field. Note that editor and related \n fields refer to the journal while translator and related fields refer to the article.\n\nRequired fields: `author`, `title`, `journaltitle`, `year/date`"
+}
diff --git a/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__hover__field_known.snap b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__hover__field_known.snap
new file mode 100644
index 0000000000..42d630c287
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__hover__field_known.snap
@@ -0,0 +1,8 @@
+---
+source: tests/lsp/text_document/hover.rs
+expression: "find_hover(r#\"\n%! main.bib\n@article{foo, author = bar}\n |\n ^^^^^^\"#)"
+---
+{
+ "kind": "markdown",
+ "value": "The author(s) of the `title`."
+}
diff --git a/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__hover__label_theorem_child_file.snap b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__hover__label_theorem_child_file.snap
new file mode 100644
index 0000000000..19b36c4ebb
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__hover__label_theorem_child_file.snap
@@ -0,0 +1,8 @@
+---
+source: tests/lsp/text_document/hover.rs
+expression: "find_hover(r#\"\n%! main.tex\n\\documentclass{article}\n\\newtheorem{lemma}{Lemma}\n\\include{child}\n\\ref{thm:foo}\n |\n ^^^^^^^\n\n%! child.tex\n\\begin{lemma}\\label{thm:foo}\n 1 + 1 = 2\n\\end{lemma}\"#)"
+---
+{
+ "kind": "plaintext",
+ "value": "Lemma"
+}
diff --git a/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__hover__label_theorem_child_file_mumber.snap b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__hover__label_theorem_child_file_mumber.snap
new file mode 100644
index 0000000000..78ccef8e46
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__hover__label_theorem_child_file_mumber.snap
@@ -0,0 +1,8 @@
+---
+source: tests/lsp/text_document/hover.rs
+expression: "find_hover(r#\"\n%! main.tex\n\\documentclass{article}\n\\newtheorem{lemma}{Lemma}\n\\include{child}\n\\ref{thm:foo}\n |\n ^^^^^^^\n\n%! child.tex\n\\begin{lemma}[Foo]\\label{thm:foo}\n 1 + 1 = 2\n\\end{lemma}\n\n%! child.aux\n\\newlabel{thm:foo}{{1}{1}{Foo}{lemma.1}{}}\"#)"
+---
+{
+ "kind": "plaintext",
+ "value": "Lemma 1 (Foo)"
+}
diff --git a/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__hover__section.snap b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__hover__section.snap
new file mode 100644
index 0000000000..c8aa265330
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__hover__section.snap
@@ -0,0 +1,8 @@
+---
+source: tests/lsp/text_document/hover.rs
+expression: "find_hover(r#\"\n%! main.tex\n\\section{Foo}\n\\label{sec:foo}\n |\n ^^^^^^^\"#)"
+---
+{
+ "kind": "plaintext",
+ "value": "Section (Foo)"
+}
diff --git a/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__hover__string_inside_reference.snap b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__hover__string_inside_reference.snap
new file mode 100644
index 0000000000..cfcc196e26
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__hover__string_inside_reference.snap
@@ -0,0 +1,8 @@
+---
+source: tests/lsp/text_document/hover.rs
+expression: "find_hover(r#\"\n%! main.bib\n@string{foo = \"Foo\"}\n@string{bar = \"Bar\"}\n@article{baz, author = bar}\n |\n ^^^\"#)"
+---
+{
+ "kind": "plaintext",
+ "value": "Bar"
+}
diff --git a/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__inlay_hint__label_definition.snap b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__inlay_hint__label_definition.snap
new file mode 100644
index 0000000000..accf4a875c
--- /dev/null
+++ b/support/texlab/tests/lsp/text_document/snapshots/lsp__text_document__inlay_hint__label_definition.snap
@@ -0,0 +1,38 @@
+---
+source: tests/lsp/text_document/inlay_hint.rs
+expression: "find_hints(r#\"\n%! main.tex\n\\documentclass{article}\n\\usepackage{caption}\n\\begin{document}\n\\section{Foo}\\label{sec:foo}\n\\section{Bar}\\label{sec:bar}\n\\subsection{Baz}\\label{sec:baz}\n\\begin{figure}\n Test\n \\label{fig:qux}\n \\caption{Qux}\n\\end{figure}\n\\end{document}\n |\n\n%! main.aux\n\\relax \n\\providecommand*\\caption@xref[2]{\\@setref\\relax\\@undefined{#1}}\n\\newlabel{fig:qux}{{\\caption@xref {fig:qux}{ on input line 15}}{1}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {1}{\\ignorespaces Qux\\relax }}{1}{}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {1}Foo}{1}{}\\protected@file@percent }\n\\newlabel{sec:foo}{{1}{1}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {2}Bar}{1}{}\\protected@file@percent }\n\\newlabel{sec:bar}{{2}{1}}\n\\@writefile{toc}{\\contentsline {subsection}{\\numberline {2.1}Baz}{1}{}\\protected@file@percent }\n\\newlabel{sec:baz}{{2.1}{1}}\n\\gdef \\@abspage@last{1}\"#)"
+---
+[
+ {
+ "position": {
+ "line": 3,
+ "character": 27
+ },
+ "label": "Section 1 (Foo)",
+ "paddingLeft": true
+ },
+ {
+ "position": {
+ "line": 4,
+ "character": 27
+ },
+ "label": "Section 2 (Bar)",
+ "paddingLeft": true
+ },
+ {
+ "position": {
+ "line": 5,
+ "character": 30
+ },
+ "label": "Subsection 2.1 (Baz)",
+ "paddingLeft": true
+ },
+ {
+ "position": {
+ "line": 8,
+ "character": 18
+ },
+ "label": "Figure fig:qux: Qux",
+ "paddingLeft": true
+ }
+]