summaryrefslogtreecommitdiff
path: root/support/texlab/tests
diff options
context:
space:
mode:
authorNorbert Preining <norbert@preining.info>2021-05-23 03:00:39 +0000
committerNorbert Preining <norbert@preining.info>2021-05-23 03:00:39 +0000
commitf1261b349e875b842745b63258c3e338cb1fe3bf (patch)
treeb5d402b3e80818cde2c079a42249f3dcb9732247 /support/texlab/tests
parent58aa1ac09b1d9e4769d0a0661cf12e2b2db41b14 (diff)
CTAN sync 202105230300
Diffstat (limited to 'support/texlab/tests')
-rw-r--r--support/texlab/tests/integration/common.rs318
-rw-r--r--support/texlab/tests/integration/completion.rs1927
-rw-r--r--support/texlab/tests/integration/definition.rs482
-rw-r--r--support/texlab/tests/integration/document_symbol.rs836
-rw-r--r--support/texlab/tests/integration/folding.rs126
-rw-r--r--support/texlab/tests/integration/highlight.rs83
-rw-r--r--support/texlab/tests/integration/hover.rs163
-rw-r--r--support/texlab/tests/integration/issues.rs389
-rw-r--r--support/texlab/tests/integration/link.rs173
-rw-r--r--support/texlab/tests/integration/main.rs9
-rw-r--r--support/texlab/tests/integration/prepare_rename.rs151
-rw-r--r--support/texlab/tests/integration/reference.rs150
-rw-r--r--support/texlab/tests/integration/rename.rs236
-rw-r--r--support/texlab/tests/integration/snapshots/integration__completion__bibtex__command_complete_entry.snap150
-rw-r--r--support/texlab/tests/integration/snapshots/integration__completion__bibtex__command_incomplete_entry.snap150
-rw-r--r--support/texlab/tests/integration/snapshots/integration__completion__bibtex__empty_document.snap9
-rw-r--r--support/texlab/tests/integration/snapshots/integration__completion__bibtex__entry_type_complete.snap33
-rw-r--r--support/texlab/tests/integration/snapshots/integration__completion__bibtex__entry_type_empty_name.snap159
-rw-r--r--support/texlab/tests/integration/snapshots/integration__completion__bibtex__entry_type_empty_name_before.snap9
-rw-r--r--support/texlab/tests/integration/snapshots/integration__completion__bibtex__entry_type_incomplete.snap79
-rw-r--r--support/texlab/tests/integration/snapshots/integration__completion__bibtex__field_complete_entry.snap171
-rw-r--r--support/texlab/tests/integration/snapshots/integration__completion__bibtex__field_incomplete_entry.snap171
-rw-r--r--support/texlab/tests/integration/snapshots/integration__completion__bibtex__junk.snap9
-rw-r--r--support/texlab/tests/integration/snapshots/integration__completion__latex__begin_command.snap162
-rw-r--r--support/texlab/tests/integration/snapshots/integration__completion__latex__citation.snap54
-rw-r--r--support/texlab/tests/integration/snapshots/integration__completion__latex__citation_after.snap9
-rw-r--r--support/texlab/tests/integration/snapshots/integration__completion__latex__citation_open_brace.snap34
-rw-r--r--support/texlab/tests/integration/snapshots/integration__completion__latex__class_command.snap150
-rw-r--r--support/texlab/tests/integration/snapshots/integration__completion__latex__class_environment.snap30
-rw-r--r--support/texlab/tests/integration/snapshots/integration__completion__latex__class_import.snap171
-rw-r--r--support/texlab/tests/integration/snapshots/integration__completion__latex__color_model_define_color.snap105
-rw-r--r--support/texlab/tests/integration/snapshots/integration__completion__latex__color_model_define_color_set.snap67
-rw-r--r--support/texlab/tests/integration/snapshots/integration__completion__latex__color_name.snap143
-rw-r--r--support/texlab/tests/integration/snapshots/integration__completion__latex__empty_document.snap9
-rw-r--r--support/texlab/tests/integration/snapshots/integration__completion__latex__kernel_command.snap30
-rw-r--r--support/texlab/tests/integration/snapshots/integration__completion__latex__kernel_command_environment.snap30
-rw-r--r--support/texlab/tests/integration/snapshots/integration__completion__latex__kernel_command_glyph.snap34
-rw-r--r--support/texlab/tests/integration/snapshots/integration__completion__latex__label.snap137
-rw-r--r--support/texlab/tests/integration/snapshots/integration__completion__latex__package_command.snap34
-rw-r--r--support/texlab/tests/integration/snapshots/integration__completion__latex__package_environment.snap150
-rw-r--r--support/texlab/tests/integration/snapshots/integration__completion__latex__package_import.snap102
-rw-r--r--support/texlab/tests/integration/snapshots/integration__completion__latex__pgf_library.snap143
-rw-r--r--support/texlab/tests/integration/snapshots/integration__completion__latex__preselect_environment.snap150
-rw-r--r--support/texlab/tests/integration/snapshots/integration__completion__latex__theorem_environment.snap130
-rw-r--r--support/texlab/tests/integration/snapshots/integration__completion__latex__user_command.snap30
-rw-r--r--support/texlab/tests/integration/snapshots/integration__completion__latex__user_environment.snap50
-rw-r--r--support/texlab/tests/integration/snapshots/integration__document_symbol__enumerate_flat.snap97
-rw-r--r--support/texlab/tests/integration/snapshots/integration__document_symbol__enumerate_nested.snap141
-rw-r--r--support/texlab/tests/integration/snapshots/integration__document_symbol__equation_flat.snap61
-rw-r--r--support/texlab/tests/integration/snapshots/integration__document_symbol__equation_nested.snap87
-rw-r--r--support/texlab/tests/integration/snapshots/integration__document_symbol__float_flat.snap61
-rw-r--r--support/texlab/tests/integration/snapshots/integration__document_symbol__float_nested.snap87
-rw-r--r--support/texlab/tests/integration/snapshots/integration__document_symbol__section_flat.snap61
-rw-r--r--support/texlab/tests/integration/snapshots/integration__document_symbol__section_nested.snap88
-rw-r--r--support/texlab/tests/integration/snapshots/integration__document_symbol__theorem_flat.snap79
-rw-r--r--support/texlab/tests/integration/snapshots/integration__document_symbol__theorem_nested.snap114
-rw-r--r--support/texlab/tests/integration/snapshots/integration__hover__empty_bibtex_document.snap6
-rw-r--r--support/texlab/tests/integration/snapshots/integration__hover__empty_labtex_document.snap6
-rw-r--r--support/texlab/tests/integration/snapshots/integration__hover__label_theorem_child_file.snap21
-rw-r--r--support/texlab/tests/integration/snapshots/integration__hover__label_theorem_child_file_mumber.snap21
-rw-r--r--support/texlab/tests/integration/snapshots/integration__workspace_symbol__filter_bibtex.snap43
-rw-r--r--support/texlab/tests/integration/snapshots/integration__workspace_symbol__filter_type_figure.snap25
-rw-r--r--support/texlab/tests/integration/snapshots/integration__workspace_symbol__filter_type_item.snap61
-rw-r--r--support/texlab/tests/integration/snapshots/integration__workspace_symbol__filter_type_math.snap43
-rw-r--r--support/texlab/tests/integration/snapshots/integration__workspace_symbol__filter_type_section.snap79
-rw-r--r--support/texlab/tests/integration/workspace_symbol.rs323
66 files changed, 5781 insertions, 3660 deletions
diff --git a/support/texlab/tests/integration/common.rs b/support/texlab/tests/integration/common.rs
new file mode 100644
index 0000000000..5430bfd2be
--- /dev/null
+++ b/support/texlab/tests/integration/common.rs
@@ -0,0 +1,318 @@
+use std::{
+ fs,
+ sync::{
+ atomic::{AtomicI32, Ordering},
+ Mutex,
+ },
+ thread::{self, JoinHandle},
+};
+
+use anyhow::Result;
+use lsp_server::{Connection, RequestId};
+use lsp_types::{notification::Notification, request::Request, *};
+use rustc_hash::FxHashMap;
+use tempfile::{tempdir, TempDir};
+use texlab::Server;
+use unindent::unindent;
+
+pub struct ServerTester {
+ pub directory: TempDir,
+ client: Connection,
+ handle: Option<JoinHandle<()>>,
+ request_id: AtomicI32,
+ pub diagnostics_by_uri: Mutex<FxHashMap<Url, Vec<Diagnostic>>>,
+}
+
+impl ServerTester {
+ pub fn launch_new_instance() -> Result<Self> {
+ let directory = tempdir()?;
+ let (conn, client) = Connection::memory();
+ let server = Server::with_connection(conn, directory.path().to_path_buf(), false)?;
+ let handle = thread::spawn(move || server.run().unwrap());
+ Ok(Self {
+ directory,
+ client,
+ handle: Some(handle),
+ request_id: AtomicI32::new(0),
+ diagnostics_by_uri: Mutex::default(),
+ })
+ }
+
+ fn wait_for_response(&self, request_id: RequestId) -> Result<lsp_server::Response> {
+ loop {
+ match self.client.receiver.recv()? {
+ lsp_server::Message::Request(request) => {
+ match request.method.as_str() {
+ request::RegisterCapability::METHOD => {
+ self.client
+ .sender
+ .send(lsp_server::Response::new_ok(request.id, ()).into())?;
+ }
+ method => {
+ panic!("unknown request: {}", method);
+ }
+ };
+ }
+ lsp_server::Message::Notification(notification) => {
+ match notification.method.as_str() {
+ notification::PublishDiagnostics::METHOD => {
+ let params = serde_json::from_value::<PublishDiagnosticsParams>(
+ notification.params,
+ )?;
+ let mut diagnostics_by_uri = self.diagnostics_by_uri.lock().unwrap();
+ diagnostics_by_uri.insert(params.uri, params.diagnostics);
+ }
+ method => {
+ panic!("unknown notification: {}", method);
+ }
+ };
+ }
+ lsp_server::Message::Response(response) => {
+ assert_eq!(response.id, request_id);
+ return Ok(response);
+ }
+ }
+ }
+ }
+
+ #[allow(deprecated)]
+ pub fn initialize(
+ &self,
+ client_capabilities: ClientCapabilities,
+ client_info: Option<ClientInfo>,
+ ) -> anyhow::Result<()> {
+ let request_id = RequestId::from(self.request_id.fetch_add(1, Ordering::SeqCst));
+ self.client.sender.send(
+ lsp_server::Request::new(
+ request_id.clone(),
+ request::Initialize::METHOD.to_string(),
+ InitializeParams {
+ process_id: None,
+ root_path: None,
+ root_uri: None,
+ initialization_options: None,
+ capabilities: client_capabilities,
+ trace: None,
+ workspace_folders: None,
+ client_info,
+ locale: None,
+ },
+ )
+ .into(),
+ )?;
+ self.wait_for_response(request_id)?;
+
+ self.client.sender.send(
+ lsp_server::Notification::new(notification::Initialized::METHOD.to_string(), ()).into(),
+ )?;
+
+ Ok(())
+ }
+
+ pub fn open(&self, name: &str, text: &str, language_id: &str, store: bool) -> Result<Url> {
+ let text = unindent(text).trim().to_string();
+ let path = self.directory.path().join(name);
+ if store {
+ fs::create_dir_all(path.parent().unwrap())?;
+ fs::write(&path, &text)?;
+ }
+
+ let uri = Url::from_file_path(path).unwrap();
+ self.client.sender.send(
+ lsp_server::Notification::new(
+ notification::DidOpenTextDocument::METHOD.to_string(),
+ DidOpenTextDocumentParams {
+ text_document: TextDocumentItem::new(
+ uri.clone(),
+ language_id.to_string(),
+ 0,
+ text,
+ ),
+ },
+ )
+ .into(),
+ )?;
+ Ok(uri)
+ }
+
+ pub fn open_memory(&self, uri: Url, text: &str, language_id: &str) -> Result<()> {
+ let text = unindent(text).trim().to_string();
+ let text_document = TextDocumentItem::new(uri, language_id.to_string(), 0, text);
+ self.client.sender.send(
+ lsp_server::Notification::new(
+ notification::DidOpenTextDocument::METHOD.to_string(),
+ DidOpenTextDocumentParams { text_document },
+ )
+ .into(),
+ )?;
+ Ok(())
+ }
+
+ // pub fn edit(&self, uri: Url, text: &str) -> Result<()> {
+ // let text = unindent(text).trim().to_string();
+ // self.client.sender.send(
+ // lsp_server::Notification::new(
+ // notification::DidChangeTextDocument::METHOD.to_string(),
+ // DidChangeTextDocumentParams {
+ // text_document: VersionedTextDocumentIdentifier::new(uri, 0),
+ // content_changes: vec![TextDocumentContentChangeEvent {
+ // text,
+ // range: None,
+ // range_length: None,
+ // }],
+ // },
+ // )
+ // .into(),
+ // )?;
+ // Ok(())
+ // }
+
+ pub fn complete(&self, uri: Url, line: u32, character: u32) -> Result<CompletionList> {
+ let request_id = RequestId::from(self.request_id.fetch_add(1, Ordering::SeqCst));
+
+ self.client.sender.send(
+ lsp_server::Request::new(
+ request_id.clone(),
+ request::Completion::METHOD.to_string(),
+ CompletionParams {
+ text_document_position: TextDocumentPositionParams::new(
+ TextDocumentIdentifier::new(uri),
+ Position::new(line, character),
+ ),
+ work_done_progress_params: WorkDoneProgressParams::default(),
+ partial_result_params: PartialResultParams::default(),
+ context: None,
+ },
+ )
+ .into(),
+ )?;
+
+ let response = self.wait_for_response(request_id)?;
+ let list = serde_json::from_value(response.result.expect("completion request failed"))?;
+ Ok(list)
+ }
+
+ pub fn resolve_completion_item(&self, item: CompletionItem) -> Result<CompletionItem> {
+ let request_id = RequestId::from(self.request_id.fetch_add(1, Ordering::SeqCst));
+
+ self.client.sender.send(
+ lsp_server::Request::new(
+ request_id.clone(),
+ request::ResolveCompletionItem::METHOD.to_string(),
+ item,
+ )
+ .into(),
+ )?;
+
+ let response = self.wait_for_response(request_id)?;
+ let result = serde_json::from_value(
+ response
+ .result
+ .expect("resolve completion item request failed"),
+ )?;
+ Ok(result)
+ }
+
+ pub fn hover(&self, uri: Url, line: u32, character: u32) -> Result<Option<Hover>> {
+ let request_id = RequestId::from(self.request_id.fetch_add(1, Ordering::SeqCst));
+
+ self.client.sender.send(
+ lsp_server::Request::new(
+ request_id.clone(),
+ request::HoverRequest::METHOD.to_string(),
+ HoverParams {
+ text_document_position_params: TextDocumentPositionParams::new(
+ TextDocumentIdentifier::new(uri),
+ Position::new(line, character),
+ ),
+ work_done_progress_params: WorkDoneProgressParams::default(),
+ },
+ )
+ .into(),
+ )?;
+
+ let response = self.wait_for_response(request_id)?;
+ let hover = serde_json::from_value(response.result.expect("hover request failed"))?;
+ Ok(hover)
+ }
+
+ pub fn find_document_symbols(&self, uri: Url) -> Result<DocumentSymbolResponse> {
+ let request_id = RequestId::from(self.request_id.fetch_add(1, Ordering::SeqCst));
+
+ self.client.sender.send(
+ lsp_server::Request::new(
+ request_id.clone(),
+ request::DocumentSymbolRequest::METHOD.to_string(),
+ DocumentSymbolParams {
+ text_document: TextDocumentIdentifier::new(uri),
+ work_done_progress_params: WorkDoneProgressParams::default(),
+ partial_result_params: PartialResultParams::default(),
+ },
+ )
+ .into(),
+ )?;
+
+ let response = self.wait_for_response(request_id)?;
+ let symbols =
+ serde_json::from_value(response.result.expect("document symbol request failed"))?;
+ Ok(symbols)
+ }
+
+ pub fn find_workspace_symbols(&self, query: &str) -> Result<Vec<SymbolInformation>> {
+ let request_id = RequestId::from(self.request_id.fetch_add(1, Ordering::SeqCst));
+
+ self.client.sender.send(
+ lsp_server::Request::new(
+ request_id.clone(),
+ request::WorkspaceSymbol::METHOD.to_string(),
+ WorkspaceSymbolParams {
+ partial_result_params: PartialResultParams::default(),
+ work_done_progress_params: WorkDoneProgressParams::default(),
+ query: query.to_string(),
+ },
+ )
+ .into(),
+ )?;
+
+ let response = self.wait_for_response(request_id)?;
+ let symbols =
+ serde_json::from_value(response.result.expect("workspace symbol request failed"))?;
+ Ok(symbols)
+ }
+
+ // pub fn change_configuration(&self, options: Options) -> Result<()> {
+ // self.client.sender.send(
+ // lsp_server::Notification::new(
+ // notification::DidChangeConfiguration::METHOD.to_string(),
+ // DidChangeConfigurationParams {
+ // settings: serde_json::to_value(options)?,
+ // },
+ // )
+ // .into(),
+ // )?;
+ // Ok(())
+ // }
+}
+
+impl Drop for ServerTester {
+ fn drop(&mut self) {
+ self.client
+ .sender
+ .send(
+ lsp_server::Request::new(
+ RequestId::from(self.request_id.fetch_add(1, Ordering::SeqCst)),
+ request::Shutdown::METHOD.to_string(),
+ (),
+ )
+ .into(),
+ )
+ .unwrap();
+
+ self.client
+ .sender
+ .send(lsp_server::Notification::new(notification::Exit::METHOD.to_string(), ()).into())
+ .unwrap();
+
+ self.handle.take().unwrap().join().unwrap();
+ }
+}
diff --git a/support/texlab/tests/integration/completion.rs b/support/texlab/tests/integration/completion.rs
index 4b3b4ecd83..33af77f003 100644
--- a/support/texlab/tests/integration/completion.rs
+++ b/support/texlab/tests/integration/completion.rs
@@ -1,1227 +1,762 @@
-#[cfg(feature = "citation")]
-use texlab::protocol::{MarkupContent, MarkupKind};
-
-use indoc::indoc;
-use itertools::Itertools;
-use texlab::{
- protocol::{CompletionItem, CompletionTextEdit, Documentation, Range, RangeExt, TextEdit},
- test::{TestBed, TestBedBuilder, TestLspClient, PULL_CAPABILITIES},
-};
-
-async fn run_item(
- test_bed: &TestBed,
- relative_path: &str,
- line: u64,
- character: u64,
- label: &str,
-) -> CompletionItem {
- let item = test_bed
- .completion(relative_path, line, character)
- .await
- .unwrap()
- .into_iter()
- .find(|item| item.label == label)
- .unwrap();
-
- test_bed.client.completion_resolve(item).await.unwrap()
-}
-
-async fn run_list(
- test_bed: &TestBed,
- relative_path: &str,
- line: u64,
- character: u64,
-) -> Vec<String> {
- test_bed
- .completion(relative_path, line, character)
- .await
- .unwrap()
- .into_iter()
- .map(|item| item.label)
- .sorted()
- .collect()
-}
-
-fn verify_text_edit(
- item: &CompletionItem,
- start_line: u64,
- start_character: u64,
- end_line: u64,
- end_character: u64,
- text: &str,
-) {
- assert_eq!(
- *item.text_edit.as_ref().unwrap(),
- CompletionTextEdit::Edit(TextEdit::new(
- Range::new_simple(start_line, start_character, end_line, end_character),
- text.into()
- ))
- );
-}
-
-fn verify_detail(item: &CompletionItem, detail: &str) {
- assert_eq!(item.detail.as_ref().unwrap(), detail);
-}
-
-#[tokio::test]
-async fn empty_latex_document() {
- let mut test_bed = TestBedBuilder::new().file("main.tex", "").build().await;
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
- test_bed.open("main.tex").await;
-
- let actual_items = test_bed.completion("main.tex", 0, 0).await.unwrap();
-
- test_bed.shutdown().await;
-
- assert!(actual_items.is_empty());
-}
-
-#[tokio::test]
-async fn empty_bibtex_document() {
- let mut test_bed = TestBedBuilder::new().file("main.bib", "").build().await;
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
- test_bed.open("main.bib").await;
-
- let actual_items = test_bed.completion("main.bib", 0, 0).await.unwrap();
-
- test_bed.shutdown().await;
-
- assert!(actual_items.is_empty());
-}
-
-#[tokio::test]
-async fn bibtex_comment() {
- let mut test_bed = TestBedBuilder::new().file("main.bib", "foo").build().await;
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
- test_bed.open("main.bib").await;
-
- let actual_items = test_bed.completion("main.bib", 0, 2).await.unwrap();
-
- test_bed.shutdown().await;
-
- assert!(actual_items.is_empty());
-}
-
-#[tokio::test]
-async fn bibtex_command_incomplete_entry() {
- let mut test_bed = TestBedBuilder::new()
- .file(
+use anyhow::Result;
+use lsp_types::{CompletionList, Url};
+
+use crate::common::ServerTester;
+
+fn complete_and_resolve(
+ server: &ServerTester,
+ uri: Url,
+ line: u32,
+ character: u32,
+) -> Result<CompletionList> {
+ let mut list = server.complete(uri, line, character)?;
+ let mut new_items = Vec::new();
+ for item in list.items.into_iter().take(7) {
+ let mut new_item = server.resolve_completion_item(item)?;
+ new_item.data = None;
+ new_items.push(new_item);
+ }
+ list.items = new_items;
+ Ok(list)
+}
+
+mod bibtex {
+ use insta::assert_json_snapshot;
+ use lsp_types::ClientCapabilities;
+
+ use super::*;
+
+ #[test]
+ fn test_empty_document() -> Result<()> {
+ let server = ServerTester::launch_new_instance()?;
+ server.initialize(ClientCapabilities::default(), None)?;
+ let uri = server.open("main.bib", "", "bibtex", false)?;
+ assert_json_snapshot!(complete_and_resolve(&server, uri, 0, 0)?);
+ Ok(())
+ }
+
+ #[test]
+ fn test_junk() -> Result<()> {
+ let server = ServerTester::launch_new_instance()?;
+ server.initialize(ClientCapabilities::default(), None)?;
+ let uri = server.open("main.bib", "foo", "bibtex", false)?;
+ assert_json_snapshot!(complete_and_resolve(&server, uri, 0, 0)?);
+ Ok(())
+ }
+
+ #[test]
+ fn test_command_incomplete_entry() -> Result<()> {
+ let server = ServerTester::launch_new_instance()?;
+ server.initialize(ClientCapabilities::default(), None)?;
+ let uri = server.open(
"main.bib",
- indoc!(
- r#"
- @article{foo,
- author = {\LaT
- }
- "#
- ),
- )
- .build()
- .await;
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
- test_bed.open("main.bib").await;
-
- let actual_item = run_item(&test_bed, "main.bib", 1, 18, "LaTeX").await;
-
- test_bed.shutdown().await;
-
- verify_detail(&actual_item, "built-in");
- verify_text_edit(&actual_item, 1, 15, 1, 18, "LaTeX");
-}
-
-#[tokio::test]
-async fn bibtex_command_complete_entry() {
- let mut test_bed = TestBedBuilder::new()
- .file(
+ r#"
+ @article{foo,
+ author = {\LaT
+ }
+ "#,
+ "bibtex",
+ false,
+ )?;
+ assert_json_snapshot!(complete_and_resolve(&server, uri, 1, 18)?);
+ Ok(())
+ }
+
+ #[test]
+ fn test_command_complete_entry() -> Result<()> {
+ let server = ServerTester::launch_new_instance()?;
+ server.initialize(ClientCapabilities::default(), None)?;
+ let uri = server.open(
"main.bib",
- indoc!(
- r#"
- @article{foo,
- author = {\LaT}
- }
- "#
- ),
- )
- .build()
- .await;
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
- test_bed.open("main.bib").await;
-
- let actual_item = run_item(&test_bed, "main.bib", 1, 18, "LaTeX").await;
-
- test_bed.shutdown().await;
-
- verify_detail(&actual_item, "built-in");
- verify_text_edit(&actual_item, 1, 15, 1, 18, "LaTeX");
-}
-
-#[tokio::test]
-async fn bibtex_type_empty() {
- let mut test_bed = TestBedBuilder::new()
- .file(
+ r#"
+ @article{foo,
+ author = {\LaT}
+ }
+ "#,
+ "bibtex",
+ false,
+ )?;
+ assert_json_snapshot!(complete_and_resolve(&server, uri, 1, 18)?);
+ Ok(())
+ }
+
+ #[test]
+ fn test_entry_type_empty_name() -> Result<()> {
+ let server = ServerTester::launch_new_instance()?;
+ server.initialize(ClientCapabilities::default(), None)?;
+ let uri = server.open(
"main.bib",
- indoc!(
- r#"
- @
- "#
- ),
- )
- .build()
- .await;
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
- test_bed.open("main.bib").await;
-
- let actual_item = run_item(&test_bed, "main.bib", 0, 1, "article").await;
-
- test_bed.shutdown().await;
-
- assert!(actual_item.documentation.is_some());
- verify_text_edit(&actual_item, 0, 1, 0, 1, "article");
-}
-
-#[tokio::test]
-async fn bibtex_type_incomplete() {
- let mut test_bed = TestBedBuilder::new()
- .file(
+ r#"
+ @
+ "#,
+ "bibtex",
+ false,
+ )?;
+ assert_json_snapshot!(complete_and_resolve(&server, uri, 0, 1)?);
+ Ok(())
+ }
+
+ #[test]
+ fn test_entry_type_empty_name_before() -> Result<()> {
+ let server = ServerTester::launch_new_instance()?;
+ server.initialize(ClientCapabilities::default(), None)?;
+ let uri = server.open(
"main.bib",
- indoc!(
- r#"
- @art
- "#
- ),
- )
- .build()
- .await;
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
- test_bed.open("main.bib").await;
-
- let actual_item = run_item(&test_bed, "main.bib", 0, 1, "article").await;
-
- test_bed.shutdown().await;
-
- assert!(actual_item.documentation.is_some());
- verify_text_edit(&actual_item, 0, 1, 0, 4, "article");
-}
-
-#[tokio::test]
-async fn bibtex_type_complete() {
- let mut test_bed = TestBedBuilder::new()
- .file(
+ r#"
+ @
+ "#,
+ "bibtex",
+ false,
+ )?;
+ assert_json_snapshot!(complete_and_resolve(&server, uri, 0, 0)?);
+ Ok(())
+ }
+
+ #[test]
+ fn test_entry_type_incomplete() -> Result<()> {
+ let server = ServerTester::launch_new_instance()?;
+ server.initialize(ClientCapabilities::default(), None)?;
+ let uri = server.open(
"main.bib",
- indoc!(
- r#"
- @article
- "#
- ),
- )
- .build()
- .await;
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
- test_bed.open("main.bib").await;
-
- let actual_item = run_item(&test_bed, "main.bib", 0, 1, "article").await;
-
- test_bed.shutdown().await;
-
- assert!(actual_item.documentation.is_some());
- verify_text_edit(&actual_item, 0, 1, 0, 8, "article");
-}
-
-#[tokio::test]
-async fn bibtex_field_incomplete_entry() {
- let mut test_bed = TestBedBuilder::new()
- .file(
+ r#"
+ @art
+ "#,
+ "bibtex",
+ false,
+ )?;
+ assert_json_snapshot!(complete_and_resolve(&server, uri, 0, 1)?);
+ Ok(())
+ }
+
+ #[test]
+ fn test_entry_type_complete() -> Result<()> {
+ let server = ServerTester::launch_new_instance()?;
+ server.initialize(ClientCapabilities::default(), None)?;
+ let uri = server.open(
"main.bib",
- indoc!(
- r#"
- @article{foo,
- titl
- "#
- ),
- )
- .build()
- .await;
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
- test_bed.open("main.bib").await;
-
- let actual_item = run_item(&test_bed, "main.bib", 1, 6, "title").await;
-
- test_bed.shutdown().await;
-
- assert!(actual_item.documentation.is_some());
- verify_text_edit(&actual_item, 1, 4, 1, 8, "title");
-}
-
-#[tokio::test]
-async fn bibtex_field_complete_entry() {
- let mut test_bed = TestBedBuilder::new()
- .file(
+ r#"
+ @article
+ "#,
+ "bibtex",
+ false,
+ )?;
+ assert_json_snapshot!(complete_and_resolve(&server, uri, 0, 1)?);
+ Ok(())
+ }
+
+ #[test]
+ fn test_field_incomplete_entry() -> Result<()> {
+ let server = ServerTester::launch_new_instance()?;
+ server.initialize(ClientCapabilities::default(), None)?;
+ let uri = server.open(
"main.bib",
- indoc!(
- r#"
- @article{foo,
- title = {}
- }
- "#
- ),
- )
- .build()
- .await;
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
- test_bed.open("main.bib").await;
-
- let actual_item = run_item(&test_bed, "main.bib", 1, 6, "title").await;
-
- test_bed.shutdown().await;
-
- assert!(actual_item.documentation.is_some());
- verify_text_edit(&actual_item, 1, 4, 1, 9, "title");
-}
-
-#[tokio::test]
-async fn latex_begin_command() {
- let mut test_bed = TestBedBuilder::new().file("main.tex", r#"\"#).build().await;
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
- test_bed.open("main.tex").await;
-
- let actual_item = run_item(&test_bed, "main.tex", 0, 1, "begin").await;
-
- test_bed.shutdown().await;
-
- verify_detail(&actual_item, "built-in");
-}
-
-#[cfg(feature = "citation")]
-#[tokio::test]
-async fn latex_citation_valid() {
- let mut test_bed = TestBedBuilder::new()
- .file(
- "main.tex",
- indoc!(
- r#"
- \documentclass{article}
- \bibliography{main}
- \begin{document}
- \cite{
- \end{document}
- "#
- ),
- )
- .file(
+ r#"
+ @article{foo,
+ titl
+ "#,
+ "bibtex",
+ false,
+ )?;
+ assert_json_snapshot!(complete_and_resolve(&server, uri, 1, 6)?);
+ Ok(())
+ }
+
+ #[test]
+ fn test_field_complete_entry() -> Result<()> {
+ let server = ServerTester::launch_new_instance()?;
+ server.initialize(ClientCapabilities::default(), None)?;
+ let uri = server.open(
"main.bib",
- indoc!(
- r#"
- @article{foo:2019,
- author = {Foo Bar},
- title = {Baz Qux},
- year = {2019},
- }
-
- @article{bar:2005,}
- "#
- ),
- )
- .build()
- .await;
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
- test_bed.open("main.tex").await;
- test_bed.open("main.bib").await;
-
- let actual_item = run_item(&test_bed, "main.tex", 3, 6, "foo:2019").await;
-
- test_bed.shutdown().await;
-
- verify_text_edit(&actual_item, 3, 6, 3, 6, "foo:2019");
- assert_eq!(
- actual_item.documentation.unwrap(),
- Documentation::MarkupContent(MarkupContent {
- kind: MarkupKind::Markdown,
- value: "Bar, F. (2019). *Baz Qux*.".into()
- })
- );
-}
-
-#[cfg(feature = "citation")]
-#[tokio::test]
-async fn latex_citation_invalid() {
- let mut test_bed = TestBedBuilder::new()
- .file(
+ r#"
+ @article{foo,
+ title = {}
+ }
+ "#,
+ "bibtex",
+ false,
+ )?;
+ assert_json_snapshot!(complete_and_resolve(&server, uri, 1, 6)?);
+ Ok(())
+ }
+}
+
+mod latex {
+ use insta::assert_json_snapshot;
+ use lsp_types::ClientCapabilities;
+
+ use super::*;
+
+ #[test]
+ fn test_empty_document() -> Result<()> {
+ let server = ServerTester::launch_new_instance()?;
+ server.initialize(ClientCapabilities::default(), None)?;
+ let uri = server.open("main.tex", "", "latex", false)?;
+ assert_json_snapshot!(complete_and_resolve(&server, uri, 0, 0)?);
+ Ok(())
+ }
+
+ #[test]
+ fn test_begin_command() -> Result<()> {
+ let server = ServerTester::launch_new_instance()?;
+ server.initialize(ClientCapabilities::default(), None)?;
+ let uri = server.open("main.tex", r#"\b"#, "latex", false)?;
+ assert_json_snapshot!(complete_and_resolve(&server, uri, 0, 1)?);
+ Ok(())
+ }
+
+ #[test]
+ fn test_citation() -> Result<()> {
+ let server = ServerTester::launch_new_instance()?;
+ server.initialize(ClientCapabilities::default(), None)?;
+ let tex_uri = server.open(
"main.tex",
- indoc!(
- r#"
- \documentclass{article}
- \bibliography{main}
- \begin{document}
- \cite{
- \end{document}
- "#
- ),
- )
- .file(
+ r#"
+ \documentclass{article}
+ \bibliography{main}
+ \begin{document}
+ \cite{
+ \end{document}
+ "#,
+ "latex",
+ false,
+ )?;
+ server.open(
"main.bib",
- indoc!(
- r#"
- @article{foo:2019,
- author = {Foo Bar},
- title = {Baz Qux},
- year = {2019},
- }
-
- @article{bar:2005,}
- "#
- ),
- )
- .build()
- .await;
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
- test_bed.open("main.tex").await;
- test_bed.open("main.bib").await;
-
- let actual_item = run_item(&test_bed, "main.tex", 3, 6, "bar:2005").await;
-
- test_bed.shutdown().await;
-
- verify_text_edit(&actual_item, 3, 6, 3, 6, "bar:2005");
- assert_eq!(actual_item.documentation, None);
-}
-
-#[tokio::test]
-async fn latex_color_name() {
- let mut test_bed = TestBedBuilder::new()
- .file(
- "main.tex",
- indoc!(
- r#"
- \color{re}
- \definecolor{foo}{
- \definecolorset{R}
- "#
- ),
- )
- .build()
- .await;
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
- test_bed.open("main.tex").await;
-
- let actual_item = run_item(&test_bed, "main.tex", 0, 9, "red").await;
-
- test_bed.shutdown().await;
-
- verify_text_edit(&actual_item, 0, 7, 0, 9, "red");
-}
-
-#[tokio::test]
-async fn latex_color_model_define_color() {
- let mut test_bed = TestBedBuilder::new()
- .file(
- "main.tex",
- indoc!(
- r#"
- \color{re}
- \definecolor{foo}{
- \definecolorset{R}
- "#
- ),
- )
- .build()
- .await;
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
- test_bed.open("main.tex").await;
-
- let actual_item = run_item(&test_bed, "main.tex", 1, 18, "rgb").await;
-
- test_bed.shutdown().await;
-
- verify_text_edit(&actual_item, 1, 18, 1, 18, "rgb");
-}
-
-#[tokio::test]
-async fn latex_model_define_color_set() {
- let mut test_bed = TestBedBuilder::new()
- .file(
+ r#"
+ @article{foo:2019,
+ author = {Foo Bar},
+ title = {Baz Qux},
+ year = {2019},
+ }
+
+ @article{bar:2005,}
+ "#,
+ "bibtex",
+ false,
+ )?;
+ assert_json_snapshot!(complete_and_resolve(&server, tex_uri, 3, 6)?);
+ Ok(())
+ }
+
+ #[test]
+ fn test_citation_after() -> Result<()> {
+ let server = ServerTester::launch_new_instance()?;
+ server.initialize(ClientCapabilities::default(), None)?;
+ let tex_uri = server.open(
"main.tex",
- indoc!(
- r#"
- \color{re}
- \definecolor{foo}{
- \definecolorset{R}
- "#
- ),
- )
- .build()
- .await;
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
- test_bed.open("main.tex").await;
-
- let actual_item = run_item(&test_bed, "main.tex", 2, 17, "RGB").await;
-
- test_bed.shutdown().await;
-
- verify_text_edit(&actual_item, 2, 16, 2, 17, "RGB");
-}
-
-#[tokio::test]
-async fn latex_component_kernel_command() {
- let mut test_bed = TestBedBuilder::new()
- .file(
- "main.tex",
- indoc!(
- r#"
- \documentclass{book}
- \usepackage{amsmath}
- \chap
- \varDel
- \begin{theind}
- \end{alig}
- \begin{doc}
- \vareps
- "#
- ),
- )
- .build()
- .await;
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
- test_bed.open("main.tex").await;
-
- let actual_item = run_item(&test_bed, "main.tex", 0, 1, "documentclass").await;
-
- test_bed.shutdown().await;
-
- verify_detail(&actual_item, "built-in");
- verify_text_edit(&actual_item, 0, 1, 0, 14, "documentclass");
-}
-
-#[tokio::test]
-async fn latex_component_kernel_command_glyph() {
- let mut test_bed = TestBedBuilder::new()
- .file(
+ r#"
+ \documentclass{article}
+ \bibliography{main}
+ \begin{document}
+ \cite{}
+ \end{document}
+ "#,
+ "latex",
+ false,
+ )?;
+ server.open(
+ "main.bib",
+ r#"
+ @article{foo:2019,
+ author = {Foo Bar},
+ title = {Baz Qux},
+ year = {2019},
+ }
+
+ @article{bar:2005,}
+ "#,
+ "bibtex",
+ false,
+ )?;
+ assert_json_snapshot!(complete_and_resolve(&server, tex_uri, 3, 7)?);
+ Ok(())
+ }
+
+ #[test]
+ fn test_citation_open_brace() -> Result<()> {
+ let server = ServerTester::launch_new_instance()?;
+ server.initialize(ClientCapabilities::default(), None)?;
+ let tex_uri = server.open(
"main.tex",
- indoc!(
- r#"
- \documentclass{book}
- \usepackage{amsmath}
- \chap
- \varDel
- \begin{theind}
- \end{alig}
- \begin{doc}
- \vareps
- "#
- ),
- )
- .build()
- .await;
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
- test_bed.open("main.tex").await;
-
- let actual_item = run_item(&test_bed, "main.tex", 7, 7, "varepsilon").await;
-
- test_bed.shutdown().await;
-
- verify_detail(&actual_item, "ε, built-in");
- verify_text_edit(&actual_item, 7, 1, 7, 7, "varepsilon");
-}
-
-#[tokio::test]
-async fn latex_component_kernel_environment() {
- let mut test_bed = TestBedBuilder::new()
- .file(
+ r#"
+ \documentclass{article}
+ \bibliography{main}
+ \begin{document}
+ \cite{Foo
+ \end{document}
+ "#,
+ "latex",
+ false,
+ )?;
+ server.open(
+ "main.bib",
+ r#"
+ @article{FooBar,
+ author = {Foo Bar},
+ title = {Baz Qux},
+ year = {2019},
+ }
+ "#,
+ "bibtex",
+ false,
+ )?;
+ assert_json_snapshot!(complete_and_resolve(&server, tex_uri, 3, 9)?);
+ Ok(())
+ }
+
+ #[test]
+ fn test_color_name() -> Result<()> {
+ let server = ServerTester::launch_new_instance()?;
+ server.initialize(ClientCapabilities::default(), None)?;
+ let uri = server.open(
"main.tex",
- indoc!(
- r#"
- \documentclass{book}
- \usepackage{amsmath}
- \chap
- \varDel
- \begin{theind}
- \end{alig}
- \begin{doc}
- \vareps
- "#
- ),
- )
- .build()
- .await;
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
- test_bed.open("main.tex").await;
-
- let actual_item = run_item(&test_bed, "main.tex", 6, 10, "document").await;
-
- test_bed.shutdown().await;
-
- verify_detail(&actual_item, "built-in");
- verify_text_edit(&actual_item, 6, 7, 6, 10, "document");
-}
-
-#[tokio::test]
-async fn latex_component_class_command() {
- let mut test_bed = TestBedBuilder::new()
- .file(
+ r#"
+ \color{re}
+ \definecolor{foo}{
+ \definecolorset{R}
+ "#,
+ "latex",
+ false,
+ )?;
+ assert_json_snapshot!(complete_and_resolve(&server, uri, 0, 9)?);
+ Ok(())
+ }
+
+ #[test]
+ fn test_color_model_define_color() -> Result<()> {
+ let server = ServerTester::launch_new_instance()?;
+ server.initialize(ClientCapabilities::default(), None)?;
+ let uri = server.open(
"main.tex",
- indoc!(
- r#"
- \documentclass{book}
- \usepackage{amsmath}
- \chap
- \varDel
- \begin{theind}
- \end{alig}
- \begin{doc}
- \vareps
- "#
- ),
- )
- .build()
- .await;
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
- test_bed.open("main.tex").await;
-
- let actual_item = run_item(&test_bed, "main.tex", 2, 5, "chapter").await;
-
- test_bed.shutdown().await;
-
- verify_detail(&actual_item, "book.cls");
- verify_text_edit(&actual_item, 2, 1, 2, 5, "chapter");
-}
-
-#[tokio::test]
-async fn latex_component_class_environment() {
- let mut test_bed = TestBedBuilder::new()
- .file(
+ r#"
+ \color{re}
+ \definecolor{foo}{
+ \definecolorset{R}
+ "#,
+ "latex",
+ false,
+ )?;
+ assert_json_snapshot!(complete_and_resolve(&server, uri, 1, 18)?);
+ Ok(())
+ }
+
+ #[test]
+ fn test_color_model_define_color_set() -> Result<()> {
+ let server = ServerTester::launch_new_instance()?;
+ server.initialize(ClientCapabilities::default(), None)?;
+ let uri = server.open(
"main.tex",
- indoc!(
- r#"
- \documentclass{book}
- \usepackage{amsmath}
- \chap
- \varDel
- \begin{theind}
- \end{alig}
- \begin{doc}
- \vareps
- "#
- ),
- )
- .build()
- .await;
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
- test_bed.open("main.tex").await;
-
- let actual_item = run_item(&test_bed, "main.tex", 4, 13, "theindex").await;
-
- test_bed.shutdown().await;
-
- verify_detail(&actual_item, "book.cls");
- verify_text_edit(&actual_item, 4, 7, 4, 13, "theindex");
-}
-
-#[tokio::test]
-async fn latex_component_package_command() {
- let mut test_bed = TestBedBuilder::new()
- .file(
+ r#"
+ \color{re}
+ \definecolor{foo}{
+ \definecolorset{R}
+ "#,
+ "latex",
+ false,
+ )?;
+ assert_json_snapshot!(complete_and_resolve(&server, uri, 2, 17)?);
+ Ok(())
+ }
+
+ #[test]
+ fn test_kernel_command() -> Result<()> {
+ let server = ServerTester::launch_new_instance()?;
+ server.initialize(ClientCapabilities::default(), None)?;
+ let uri = server.open(
"main.tex",
- indoc!(
- r#"
- \documentclass{book}
- \usepackage{amsmath}
- \chap
- \varDel
- \begin{theind}
- \end{alig}
- \begin{doc}
- \vareps
- "#
- ),
- )
- .build()
- .await;
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
- test_bed.open("main.tex").await;
-
- let actual_item = run_item(&test_bed, "main.tex", 3, 7, "varDelta").await;
-
- test_bed.shutdown().await;
-
- verify_detail(&actual_item, "amsmath.sty");
- verify_text_edit(&actual_item, 3, 1, 3, 7, "varDelta");
-}
-
-#[tokio::test]
-async fn latex_component_package_environment() {
- let mut test_bed = TestBedBuilder::new()
- .file(
+ r#"
+ \documentclass{book}
+ \usepackage{amsmath}
+ \chap
+ \varDel
+ \begin{theind}
+ \end{alig}
+ \begin{doc}
+ \vareps
+ "#,
+ "latex",
+ false,
+ )?;
+ assert_json_snapshot!(complete_and_resolve(&server, uri, 0, 1)?);
+ Ok(())
+ }
+
+ #[test]
+ fn test_kernel_command_glyph() -> Result<()> {
+ let server = ServerTester::launch_new_instance()?;
+ server.initialize(ClientCapabilities::default(), None)?;
+ let uri = server.open(
"main.tex",
- indoc!(
- r#"
- \documentclass{book}
- \usepackage{amsmath}
- \chap
- \varDel
- \begin{theind}
- \end{alig}
- \begin{doc}
- \vareps
- "#
- ),
- )
- .build()
- .await;
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
- test_bed.open("main.tex").await;
-
- let actual_item = run_item(&test_bed, "main.tex", 5, 5, "align").await;
-
- test_bed.shutdown().await;
-
- verify_detail(&actual_item, "amsmath.sty");
- verify_text_edit(&actual_item, 5, 5, 5, 9, "align");
-}
-
-#[tokio::test]
-async fn latex_import_class() {
- let mut test_bed = TestBedBuilder::new()
- .file(
+ r#"
+ \documentclass{book}
+ \usepackage{amsmath}
+ \chap
+ \varDel
+ \begin{theind}
+ \end{alig}
+ \begin{doc}
+ \vareps
+ "#,
+ "latex",
+ false,
+ )?;
+ assert_json_snapshot!(complete_and_resolve(&server, uri, 7, 7)?);
+ Ok(())
+ }
+
+ #[test]
+ fn test_kernel_command_environment() -> Result<()> {
+ let server = ServerTester::launch_new_instance()?;
+ server.initialize(ClientCapabilities::default(), None)?;
+ let uri = server.open(
"main.tex",
- indoc!(
- r#"
- \documentclass{book}
- \usepackage{amsmath}
- "#
- ),
- )
- .build()
- .await;
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
- test_bed.open("main.tex").await;
-
- let actual_item = run_item(&test_bed, "main.tex", 0, 19, "book").await;
-
- test_bed.shutdown().await;
-
- assert!(actual_item.documentation.is_some());
- verify_text_edit(&actual_item, 0, 15, 0, 19, "book");
-}
-
-#[tokio::test]
-async fn latex_import_package() {
- let mut test_bed = TestBedBuilder::new()
- .file(
+ r#"
+ \documentclass{book}
+ \usepackage{amsmath}
+ \chap
+ \varDel
+ \begin{theind}
+ \end{alig}
+ \begin{doc}
+ \vareps
+ "#,
+ "latex",
+ false,
+ )?;
+ assert_json_snapshot!(complete_and_resolve(&server, uri, 6, 10)?);
+ Ok(())
+ }
+
+ #[test]
+ fn test_class_command() -> Result<()> {
+ let server = ServerTester::launch_new_instance()?;
+ server.initialize(ClientCapabilities::default(), None)?;
+ let uri = server.open(
"main.tex",
- indoc!(
- r#"
- \documentclass{book}
- \usepackage{amsmath}
- "#
- ),
- )
- .build()
- .await;
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
- test_bed.open("main.tex").await;
-
- let actual_item = run_item(&test_bed, "main.tex", 1, 15, "amsmath").await;
-
- test_bed.shutdown().await;
-
- assert!(actual_item.documentation.is_some());
- verify_text_edit(&actual_item, 1, 12, 1, 19, "amsmath");
-}
-
-#[tokio::test]
-async fn latex_include_relative_root_no_extension() {
- let mut test_bed = TestBedBuilder::new()
- .file(
+ r#"
+ \documentclass{book}
+ \usepackage{amsmath}
+ \chap
+ \varDel
+ \begin{theind}
+ \end{alig}
+ \begin{doc}
+ \vareps
+ "#,
+ "latex",
+ false,
+ )?;
+ assert_json_snapshot!(complete_and_resolve(&server, uri, 2, 5)?);
+ Ok(())
+ }
+
+ #[test]
+ fn test_class_environment() -> Result<()> {
+ let server = ServerTester::launch_new_instance()?;
+ server.initialize(ClientCapabilities::default(), None)?;
+ let uri = server.open(
"main.tex",
- indoc!(
- r#"
- \documentclass{article}
- \include{}
- \input{}
- \input{qux/}
- \addbibresource{}
- "#
- ),
- )
- .file("foo.bib", "")
- .file("bar.tex", "")
- .file("qux/baz.tex", "")
- .build()
- .await;
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
- test_bed.open("main.tex").await;
-
- let actual_items = run_list(&test_bed, "main.tex", 1, 9).await;
-
- test_bed.shutdown().await;
-
- assert_eq!(actual_items, vec!["bar", "main", "qux"]);
-}
-
-#[tokio::test]
-async fn latex_include_relative_root_with_extension() {
- let mut test_bed = TestBedBuilder::new()
- .file(
+ r#"
+ \documentclass{book}
+ \usepackage{amsmath}
+ \chap
+ \varDel
+ \begin{theind}
+ \end{alig}
+ \begin{doc}
+ \vareps
+ "#,
+ "latex",
+ false,
+ )?;
+ assert_json_snapshot!(complete_and_resolve(&server, uri, 4, 13)?);
+ Ok(())
+ }
+
+ #[test]
+ fn test_package_command() -> Result<()> {
+ let server = ServerTester::launch_new_instance()?;
+ server.initialize(ClientCapabilities::default(), None)?;
+ let uri = server.open(
"main.tex",
- indoc!(
- r#"
- \documentclass{article}
- \include{}
- \input{}
- \input{qux/}
- \addbibresource{}
- "#
- ),
- )
- .file("foo.bib", "")
- .file("bar.tex", "")
- .file("qux/baz.tex", "")
- .build()
- .await;
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
- test_bed.open("main.tex").await;
-
- let actual_items = run_list(&test_bed, "main.tex", 2, 7).await;
-
- test_bed.shutdown().await;
-
- assert_eq!(actual_items, vec!["bar.tex", "main.tex", "qux"]);
-}
-
-#[tokio::test]
-async fn latex_include_relative_subdir() {
- let mut test_bed = TestBedBuilder::new()
- .file(
+ r#"
+ \documentclass{book}
+ \usepackage{amsmath}
+ \chap
+ \varDel
+ \begin{theind}
+ \end{alig}
+ \begin{doc}
+ \vareps
+ "#,
+ "latex",
+ false,
+ )?;
+ assert_json_snapshot!(complete_and_resolve(&server, uri, 3, 7)?);
+ Ok(())
+ }
+
+ #[test]
+ fn test_package_environment() -> Result<()> {
+ let server = ServerTester::launch_new_instance()?;
+ server.initialize(ClientCapabilities::default(), None)?;
+ let uri = server.open(
"main.tex",
- indoc!(
- r#"
- \documentclass{article}
- \include{}
- \input{}
- \input{qux/}
- \addbibresource{}
- "#
- ),
- )
- .file("foo.bib", "")
- .file("bar.tex", "")
- .file("qux/baz.tex", "")
- .build()
- .await;
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
- test_bed.open("main.tex").await;
-
- let actual_items = run_list(&test_bed, "main.tex", 3, 11).await;
-
- test_bed.shutdown().await;
-
- assert_eq!(actual_items, vec!["baz.tex"]);
-}
-
-#[tokio::test]
-async fn latex_include_relative_parent_dir() {
- let mut test_bed = TestBedBuilder::new()
- .file(
+ r#"
+ \documentclass{book}
+ \usepackage{amsmath}
+ \chap
+ \varDel
+ \begin{theind}
+ \end{alig}
+ \begin{doc}
+ \vareps
+ "#,
+ "latex",
+ false,
+ )?;
+ assert_json_snapshot!(complete_and_resolve(&server, uri, 5, 5)?);
+ Ok(())
+ }
+
+ #[test]
+ fn test_class_import() -> Result<()> {
+ let server = ServerTester::launch_new_instance()?;
+ server.initialize(ClientCapabilities::default(), None)?;
+ let uri = server.open(
"main.tex",
- indoc!(
- r#"
- \documentclass{article}
- \include{}
- \input{}
- \input{qux/}
- \addbibresource{}
- "#
- ),
- )
- .file("foo.bib", "")
- .file("bar.tex", "")
- .file("qux/baz.tex", r#"\input{../}"#)
- .build()
- .await;
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
- test_bed.open("qux/baz.tex").await;
-
- let actual_items = run_list(&test_bed, "qux/baz.tex", 0, 10).await;
-
- test_bed.shutdown().await;
-
- assert_eq!(actual_items, vec!["bar.tex", "main.tex", "qux"]);
-}
-
-#[tokio::test]
-async fn latex_include_relative_bibliography() {
- let mut test_bed = TestBedBuilder::new()
- .file(
+ r#"
+ \documentclass{book}
+ \usepackage{amsmath}
+ "#,
+ "latex",
+ false,
+ )?;
+ assert_json_snapshot!(complete_and_resolve(&server, uri, 0, 19)?);
+ Ok(())
+ }
+
+ #[test]
+ fn test_package_import() -> Result<()> {
+ let server = ServerTester::launch_new_instance()?;
+ server.initialize(ClientCapabilities::default(), None)?;
+ let uri = server.open(
"main.tex",
- indoc!(
- r#"
- \documentclass{article}
- \include{}
- \input{}
- \input{qux/}
- \addbibresource{}
- "#
- ),
- )
- .file("foo.bib", "")
- .file("bar.tex", "")
- .file("qux/baz.tex", "")
- .build()
- .await;
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
- test_bed.open("main.tex").await;
-
- let actual_items = run_list(&test_bed, "main.tex", 4, 16).await;
-
- test_bed.shutdown().await;
-
- assert_eq!(actual_items, vec!["foo.bib", "qux"]);
-}
-
-#[tokio::test]
-async fn latex_include_root_dir() {
- let mut test_bed = TestBedBuilder::new()
- .file("src/main.tex", r#"\input{}"#)
- .root_dir(".")
- .build()
- .await;
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
- test_bed.open("src/main.tex").await;
-
- let actual_items = run_list(&test_bed, "src/main.tex", 0, 7).await;
-
- test_bed.shutdown().await;
-
- assert_eq!(actual_items, vec!["src"]);
-}
-
-#[tokio::test]
-async fn latex_label() {
- let mut test_bed = TestBedBuilder::new()
- .file(
+ r#"
+ \documentclass{book}
+ \usepackage{amsmath}
+ "#,
+ "latex",
+ false,
+ )?;
+ assert_json_snapshot!(complete_and_resolve(&server, uri, 1, 15)?);
+ Ok(())
+ }
+
+ #[test]
+ fn test_label() -> Result<()> {
+ let server = ServerTester::launch_new_instance()?;
+ server.initialize(ClientCapabilities::default(), None)?;
+
+ server.open(
"foo.tex",
- indoc!(
- r#"
- \documentclass{article}
- %
- \usepackage{amsmath}
- \usepackage{caption}
- \usepackage{amsthm}
- \newtheorem{lemma}{Lemma}
- %
- \begin{document}
- %
- \section{Foo}%
- \label{sec:foo}
- %
- \begin{equation}%
- \label{eq:foo}
- 1 + 1 = 2
- \end{equation}
- %
- \begin{equation}%
- \label{eq:bar}
- 1 + 1 = 2
- \end{equation}
- %
- \begin{figure}%
- \LaTeX{}
- \caption{Baz}%
- \label{fig:baz}
- \end{figure}
- %
- \begin{lemma}%
- \label{thm:foo}
- 1 + 1 = 2
- \end{lemma}
- %
- \include{bar}
- %
- \end{document}
- "#
- ),
- )
- .file(
- "foo.aux",
- indoc!(
- r#"
- \relax
- \@writefile{lof}{\contentsline {figure}{\numberline {1}{\ignorespaces Baz\relax }}{1}\protected@file@percent }
- \providecommand*\caption@xref[2]{\@setref\relax\@undefined{#1}}
- \newlabel{fig:baz}{{1}{1}}
- \@writefile{toc}{\contentsline {section}{\numberline {1}Foo}{1}\protected@file@percent }
- \newlabel{sec:foo}{{1}{1}}
- \newlabel{eq:foo}{{1}{1}}
- \newlabel{eq:bar}{{2}{1}}
- \newlabel{thm:foo}{{1}{1}}
- \@input{bar.aux}
- "#
- ),
- )
- .file(
- "bar.tex",
- indoc!(
- r#"
- \section{Bar}%
- \label{sec:bar}
- %
- Lorem ipsum dolor sit amet.
- \ref{}
- \eqref{}
- "#
- ),
- )
- .file(
- "bar.aux",
- indoc!(
- r#"
- \relax
- \@writefile{toc}{\contentsline {section}{\numberline {2}Bar}{2}\protected@file@percent }
- \newlabel{sec:bar}{{2}{2}}
- \@setckpt{bar}{
- \setcounter{page}{3}
- \setcounter{equation}{2}
- \setcounter{enumi}{0}
- \setcounter{enumii}{0}
- \setcounter{enumiii}{0}
- \setcounter{enumiv}{0}
- \setcounter{footnote}{0}
- \setcounter{mpfootnote}{0}
- \setcounter{part}{0}
- \setcounter{section}{2}
- \setcounter{subsection}{0}
- \setcounter{subsubsection}{0}
- \setcounter{paragraph}{0}
- \setcounter{subparagraph}{0}
- \setcounter{figure}{1}
- \setcounter{table}{0}
- \setcounter{parentequation}{0}
- \setcounter{caption@flags}{0}
- \setcounter{ContinuedFloat}{0}
- \setcounter{lemma}{1}
- }
- "#),
- )
- .build()
- .await;
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
- test_bed.open("foo.tex").await;
- test_bed.open("foo.aux").await;
- test_bed.open("bar.tex").await;
- test_bed.open("bar.aux").await;
-
- let actual_items = test_bed.completion("bar.tex", 4, 5).await.unwrap();
-
- test_bed.shutdown().await;
-
- assert_eq!(actual_items.len(), 6);
- verify_text_edit(&actual_items[0], 4, 5, 4, 5, "sec:bar");
- verify_text_edit(&actual_items[1], 4, 5, 4, 5, "sec:foo");
- verify_text_edit(&actual_items[2], 4, 5, 4, 5, "eq:foo");
- verify_text_edit(&actual_items[3], 4, 5, 4, 5, "eq:bar");
- verify_text_edit(&actual_items[4], 4, 5, 4, 5, "fig:baz");
- verify_text_edit(&actual_items[5], 4, 5, 4, 5, "thm:foo");
- verify_detail(&actual_items[0], "Section 2 (Bar)");
- verify_detail(&actual_items[1], "Section 1 (Foo)");
- verify_detail(&actual_items[2], "Equation (1)");
- verify_detail(&actual_items[3], "Equation (2)");
- verify_detail(&actual_items[4], "Figure 1");
- verify_detail(&actual_items[5], "Lemma 1");
- assert_eq!(
- *actual_items[4].documentation.as_ref().unwrap(),
- Documentation::String("Baz".into())
- );
-}
-
-#[tokio::test]
-async fn latex_preselect_environment() {
- let mut test_bed = TestBedBuilder::new()
- .file(
- "main.tex",
- indoc!(
- r#"
- \begin{document}
- \end{
- "#
- ),
- )
- .build()
- .await;
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
- test_bed.open("main.tex").await;
-
- let actual_item = run_item(&test_bed, "main.tex", 1, 5, "document").await;
-
- test_bed.shutdown().await;
-
- assert!(actual_item.preselect.unwrap());
-}
-
-#[tokio::test]
-async fn latex_theorem() {
- let mut test_bed = TestBedBuilder::new()
- .file(
+ r#"
+ \documentclass{article}
+
+ \usepackage{amsmath}
+ \usepackage{caption}
+ \usepackage{amsthm}
+ \newtheorem{lemma}{Lemma}
+
+ \begin{document}
+
+ \section{Foo}%
+ \label{sec:foo}
+
+ \begin{equation}%
+ \label{eq:foo}
+ 1 + 1 = 2
+ \end{equation}
+
+ \begin{equation}%
+ \label{eq:bar}
+ 1 + 1 = 2
+ \end{equation}
+
+ \begin{figure}%
+ \LaTeX{}
+ \caption{Baz}%
+ \label{fig:baz}
+ \end{figure}
+
+ \begin{lemma}%
+ \label{thm:foo}
+ 1 + 1 = 2
+ \end{lemma}
+
+ \include{bar}
+
+ \end{document}
+ "#,
+ "latex",
+ true,
+ )?;
+ server.open(
+ "foo.aux",
+ r#"
+ \relax
+ \@writefile{lof}{\contentsline {figure}{\numberline {1}{\ignorespaces Baz\relax }}{1}\protected@file@percent }
+ \providecommand*\caption@xref[2]{\@setref\relax\@undefined{#1}}
+ \newlabel{fig:baz}{{1}{1}}
+ \@writefile{toc}{\contentsline {section}{\numberline {1}Foo}{1}\protected@file@percent }
+ \newlabel{sec:foo}{{1}{1}}
+ \newlabel{eq:foo}{{1}{1}}
+ \newlabel{eq:bar}{{2}{1}}
+ \newlabel{thm:foo}{{1}{1}}
+ \@input{bar.aux}
+ "#,
+ "latex",
+ true,
+ )?;
+ let uri = server.open(
+ "bar.tex",
+ r#"
+ \section{Bar}%
+ \label{sec:bar}
+
+ Lorem ipsum dolor sit amet.
+ \ref{}
+ \eqref{}
+ "#,
+ "latex",
+ true,
+ )?;
+ server.open(
+ "bar.aux",
+ r#"
+ \relax
+ \@writefile{toc}{\contentsline {section}{\numberline {2}Bar}{2}\protected@file@percent }
+ \newlabel{sec:bar}{{2}{2}}
+ \@setckpt{bar}{
+ \setcounter{page}{3}
+ \setcounter{equation}{2}
+ \setcounter{enumi}{0}
+ \setcounter{enumii}{0}
+ \setcounter{enumiii}{0}
+ \setcounter{enumiv}{0}
+ \setcounter{footnote}{0}
+ \setcounter{mpfootnote}{0}
+ \setcounter{part}{0}
+ \setcounter{section}{2}
+ \setcounter{subsection}{0}
+ \setcounter{subsubsection}{0}
+ \setcounter{paragraph}{0}
+ \setcounter{subparagraph}{0}
+ \setcounter{figure}{1}
+ \setcounter{table}{0}
+ \setcounter{parentequation}{0}
+ \setcounter{caption@flags}{0}
+ \setcounter{ContinuedFloat}{0}
+ \setcounter{lemma}{1}
+ "#,
+ "latex",
+ true,
+ )?;
+ assert_json_snapshot!(complete_and_resolve(&server, uri, 4, 5)?);
+ Ok(())
+ }
+
+ #[test]
+ fn test_preselect_environment() -> Result<()> {
+ let server = ServerTester::launch_new_instance()?;
+ server.initialize(ClientCapabilities::default(), None)?;
+ let uri = server.open(
"main.tex",
- indoc!(
- r#"
- \documentclass{article}
- \usepackage{amsthm}
- \newtheorem{foo}{Foo}
- \begin{f}
- "#
- ),
- )
- .build()
- .await;
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
- test_bed.open("main.tex").await;
-
- let actual_item = run_item(&test_bed, "main.tex", 3, 7, "foo").await;
-
- test_bed.shutdown().await;
-
- verify_text_edit(&actual_item, 3, 7, 3, 8, "foo");
- verify_detail(&actual_item, "user-defined");
-}
-
-#[tokio::test]
-async fn latex_pgf_library() {
- let mut test_bed = TestBedBuilder::new()
- .file(
+ r#"
+ \begin{document}
+ \end{
+ "#,
+ "latex",
+ false,
+ )?;
+ assert_json_snapshot!(complete_and_resolve(&server, uri, 1, 5)?);
+ Ok(())
+ }
+
+ #[test]
+ fn test_theorem_environment() -> Result<()> {
+ let server = ServerTester::launch_new_instance()?;
+ server.initialize(ClientCapabilities::default(), None)?;
+ let uri = server.open(
"main.tex",
- indoc!(
- r#"
- \usepackage{tikz}
- \usepgflibrary{}
- \usetikzlibrary{}
- "#
- ),
- )
- .build()
- .await;
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
- test_bed.open("main.tex").await;
-
- let actual_item = run_item(&test_bed, "main.tex", 1, 15, "arrows").await;
-
- test_bed.shutdown().await;
-
- verify_text_edit(&actual_item, 1, 15, 1, 15, "arrows");
-}
-
-#[tokio::test]
-async fn latex_tikz_library() {
- let mut test_bed = TestBedBuilder::new()
- .file(
+ r#"
+ \documentclass{article}
+ \usepackage{amsthm}
+ \newtheorem{foo}{Foo}
+ \begin{f}
+ "#,
+ "latex",
+ false,
+ )?;
+ assert_json_snapshot!(complete_and_resolve(&server, uri, 3, 7)?);
+ Ok(())
+ }
+
+ #[test]
+ fn test_pgf_library() -> Result<()> {
+ let server = ServerTester::launch_new_instance()?;
+ server.initialize(ClientCapabilities::default(), None)?;
+ let uri = server.open(
"main.tex",
- indoc!(
- r#"
- \usepackage{tikz}
- \usepgflibrary{}
- \usetikzlibrary{}
- "#
- ),
- )
- .build()
- .await;
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
- test_bed.open("main.tex").await;
-
- let actual_item = run_item(&test_bed, "main.tex", 2, 16, "arrows").await;
-
- test_bed.shutdown().await;
-
- verify_text_edit(&actual_item, 2, 16, 2, 16, "arrows");
-}
-
-#[tokio::test]
-async fn latex_user_command() {
- let mut test_bed = TestBedBuilder::new()
- .file(
+ r#"
+ \usepackage{tikz}
+ \usepgflibrary{}
+ \usetikzlibrary{}
+ "#,
+ "latex",
+ false,
+ )?;
+ assert_json_snapshot!(complete_and_resolve(&server, uri, 1, 15)?);
+ Ok(())
+ }
+
+ #[test]
+ fn test_user_command() -> Result<()> {
+ let server = ServerTester::launch_new_instance()?;
+ server.initialize(ClientCapabilities::default(), None)?;
+ let uri = server.open(
"main.tex",
- indoc!(
- r#"
- \foo
- \fo
- \begin{foo}
- \end{foo}
- \begin{fo}
- "#
- ),
- )
- .build()
- .await;
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
- test_bed.open("main.tex").await;
-
- let actual_item = run_item(&test_bed, "main.tex", 1, 3, "foo").await;
-
- test_bed.shutdown().await;
-
- verify_detail(&actual_item, "user-defined");
- verify_text_edit(&actual_item, 1, 1, 1, 3, "foo");
-}
-
-#[tokio::test]
-async fn latex_user_environment() {
- let mut test_bed = TestBedBuilder::new()
- .file(
+ r#"
+ \foobar
+ \fooba
+ \begin{foo}
+ \end{foo}
+ \begin{fo}
+ "#,
+ "latex",
+ false,
+ )?;
+ assert_json_snapshot!(complete_and_resolve(&server, uri, 1, 3)?);
+ Ok(())
+ }
+
+ #[test]
+ fn test_user_environment() -> Result<()> {
+ let server = ServerTester::launch_new_instance()?;
+ server.initialize(ClientCapabilities::default(), None)?;
+ let uri = server.open(
"main.tex",
- indoc!(
- r#"
- \foo
- \fo
- \begin{foo}
- \end{foo}
- \begin{fo}
- "#
- ),
- )
- .build()
- .await;
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
- test_bed.open("main.tex").await;
-
- let actual_item = run_item(&test_bed, "main.tex", 4, 7, "foo").await;
-
- test_bed.shutdown().await;
-
- verify_detail(&actual_item, "user-defined");
- verify_text_edit(&actual_item, 4, 7, 4, 9, "foo");
+ r#"
+ \foobar
+ \fooba
+ \begin{foo}
+ \end{foo}
+ \begin{fo}
+ "#,
+ "latex",
+ false,
+ )?;
+ assert_json_snapshot!(complete_and_resolve(&server, uri, 4, 7)?);
+ Ok(())
+ }
}
diff --git a/support/texlab/tests/integration/definition.rs b/support/texlab/tests/integration/definition.rs
deleted file mode 100644
index 685b121377..0000000000
--- a/support/texlab/tests/integration/definition.rs
+++ /dev/null
@@ -1,482 +0,0 @@
-use indoc::indoc;
-use texlab::{
- protocol::{LocationLink, Range, RangeExt},
- test::{TestBedBuilder, LOCATION_LINK_CAPABILITIES, PULL_CAPABILITIES},
-};
-
-fn verify_origin_selection_range(
- link: &LocationLink,
- start_line: u64,
- start_character: u64,
- end_line: u64,
- end_character: u64,
-) {
- assert_eq!(
- link.origin_selection_range,
- Some(Range::new_simple(
- start_line,
- start_character,
- end_line,
- end_character
- ))
- );
-}
-
-#[tokio::test]
-async fn empty_latex_document() {
- let mut test_bed = TestBedBuilder::new().file("main.tex", "").build().await;
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
- test_bed.open("main.tex").await;
-
- let actual_locations = test_bed
- .definition_location("main.tex", 0, 0)
- .await
- .unwrap();
-
- test_bed.shutdown().await;
-
- assert!(actual_locations.is_empty());
-}
-
-#[tokio::test]
-async fn empty_bibtex_document() {
- let mut test_bed = TestBedBuilder::new().file("main.bib", "").build().await;
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
- test_bed.open("main.bib").await;
-
- let actual_locations = test_bed
- .definition_location("main.bib", 0, 0)
- .await
- .unwrap();
-
- test_bed.shutdown().await;
-
- assert!(actual_locations.is_empty());
-}
-
-#[tokio::test]
-async fn bibtex_string() {
- let mut test_bed = TestBedBuilder::new()
- .file(
- "main.bib",
- indoc!(
- r#"
- @string{foo = "Foo"}
- %
- @string{bar = "Bar"}
- %
- @article{baz,
- author = bar
- }
- "#
- ),
- )
- .build()
- .await;
- test_bed.spawn();
- test_bed
- .initialize(LOCATION_LINK_CAPABILITIES.clone())
- .await;
- test_bed.open("main.bib").await;
-
- let mut actual_links = test_bed.definition_link("main.bib", 5, 14).await.unwrap();
-
- test_bed.shutdown().await;
-
- assert_eq!(actual_links.len(), 1);
- let link = actual_links.pop().unwrap();
- verify_origin_selection_range(&link, 5, 13, 5, 16);
- assert_eq!(link.target_uri, test_bed.uri("main.bib").into());
- assert_eq!(link.target_range, Range::new_simple(2, 0, 2, 20));
- assert_eq!(link.target_selection_range, Range::new_simple(2, 8, 2, 11));
-}
-
-#[tokio::test]
-async fn latex_citation_link() {
- let mut test_bed = TestBedBuilder::new()
- .file(
- "foo.tex",
- indoc!(
- r#"
- \bibliography{bar}
- \cite{bar}
- "#
- ),
- )
- .file(
- "bar.bib",
- indoc!(
- r#"
- @article{foo,}
- %
- @article{bar,}
- "#
- ),
- )
- .file(
- "baz.bib",
- indoc!(
- r#"
- @article{foo,}
- %
- @article{bar,}
- "#
- ),
- )
- .build()
- .await;
- test_bed.spawn();
- test_bed
- .initialize(LOCATION_LINK_CAPABILITIES.clone())
- .await;
- test_bed.open("foo.tex").await;
- test_bed.open("bar.bib").await;
- test_bed.open("baz.bib").await;
-
- let mut actual_links = test_bed.definition_link("foo.tex", 1, 7).await.unwrap();
-
- test_bed.shutdown().await;
-
- assert_eq!(actual_links.len(), 1);
- let link = actual_links.pop().unwrap();
- verify_origin_selection_range(&link, 1, 6, 1, 9);
- assert_eq!(link.target_uri, test_bed.uri("bar.bib").into());
- assert_eq!(link.target_range, Range::new_simple(2, 0, 2, 14));
- assert_eq!(link.target_selection_range, Range::new_simple(2, 9, 2, 12));
-}
-
-#[tokio::test]
-async fn latex_citation_location() {
- let mut test_bed = TestBedBuilder::new()
- .file(
- "foo.tex",
- indoc!(
- r#"
- \bibliography{bar}
- \cite{bar}
- "#
- ),
- )
- .file(
- "bar.bib",
- indoc!(
- r#"
- @article{foo,}
- %
- @article{bar,}
- "#
- ),
- )
- .file(
- "baz.bib",
- indoc!(
- r#"
- @article{foo,}
- %
- @article{bar,}
- "#
- ),
- )
- .build()
- .await;
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
- test_bed.open("foo.tex").await;
- test_bed.open("bar.bib").await;
- test_bed.open("baz.bib").await;
-
- let mut actual_locations = test_bed.definition_location("foo.tex", 1, 7).await.unwrap();
-
- test_bed.shutdown().await;
-
- assert_eq!(actual_locations.len(), 1);
- let location = actual_locations.pop().unwrap();
- assert_eq!(location.uri, test_bed.uri("bar.bib").into());
- assert_eq!(location.range, Range::new_simple(2, 9, 2, 12));
-}
-
-#[tokio::test]
-async fn latex_command_link() {
- let mut test_bed = TestBedBuilder::new()
- .file(
- "main.tex",
- indoc!(
- r#"
- \newcommand{\foo}{Foo}
- %
- \foo
- "#
- ),
- )
- .build()
- .await;
- test_bed.spawn();
- test_bed
- .initialize(LOCATION_LINK_CAPABILITIES.clone())
- .await;
- test_bed.open("main.tex").await;
-
- let mut actual_links = test_bed.definition_link("main.tex", 2, 2).await.unwrap();
-
- test_bed.shutdown().await;
-
- assert_eq!(actual_links.len(), 1);
- let link = actual_links.pop().unwrap();
- verify_origin_selection_range(&link, 2, 0, 2, 4);
- assert_eq!(link.target_uri, test_bed.uri("main.tex").into());
- assert_eq!(link.target_range, Range::new_simple(0, 0, 0, 22));
- assert_eq!(link.target_selection_range, Range::new_simple(0, 0, 0, 22));
-}
-
-#[tokio::test]
-async fn latex_math_operator_link() {
- let mut test_bed = TestBedBuilder::new()
- .file(
- "main.tex",
- indoc!(
- r#"
- \DeclareMathOperator{\foo}{foo}
- %
- \foo
- "#
- ),
- )
- .build()
- .await;
- test_bed.spawn();
- test_bed
- .initialize(LOCATION_LINK_CAPABILITIES.clone())
- .await;
- test_bed.open("main.tex").await;
-
- let mut actual_links = test_bed.definition_link("main.tex", 2, 2).await.unwrap();
-
- test_bed.shutdown().await;
-
- assert_eq!(actual_links.len(), 1);
- let link = actual_links.pop().unwrap();
- verify_origin_selection_range(&link, 2, 0, 2, 4);
- assert_eq!(link.target_uri, test_bed.uri("main.tex").into());
- assert_eq!(link.target_range, Range::new_simple(0, 0, 0, 31));
- assert_eq!(link.target_selection_range, Range::new_simple(0, 0, 0, 31));
-}
-
-#[tokio::test]
-async fn latex_label_unknown_link() {
- let mut test_bed = TestBedBuilder::new()
- .file(
- "main.tex",
- indoc!(
- r#"
- \label{foo}
- \ref{foo}
- "#
- ),
- )
- .build()
- .await;
- test_bed.spawn();
- test_bed
- .initialize(LOCATION_LINK_CAPABILITIES.clone())
- .await;
- test_bed.open("main.tex").await;
-
- let mut actual_links = test_bed.definition_link("main.tex", 1, 7).await.unwrap();
-
- test_bed.shutdown().await;
-
- assert_eq!(actual_links.len(), 1);
- let link = actual_links.pop().unwrap();
- verify_origin_selection_range(&link, 1, 5, 1, 8);
- assert_eq!(link.target_uri, test_bed.uri("main.tex").into());
- assert_eq!(link.target_range, Range::new_simple(0, 0, 0, 11));
- assert_eq!(link.target_selection_range, Range::new_simple(0, 0, 0, 11));
-}
-
-#[tokio::test]
-async fn latex_label_equation_link() {
- let mut test_bed = TestBedBuilder::new()
- .file(
- "main.tex",
- indoc!(
- r#"
- \begin{equation}%
- \label{eq:foo}
- Foo
- \end{equation}
- %
- \ref{eq:foo}
- "#
- ),
- )
- .build()
- .await;
- test_bed.spawn();
- test_bed
- .initialize(LOCATION_LINK_CAPABILITIES.clone())
- .await;
- test_bed.open("main.tex").await;
-
- let mut actual_links = test_bed.definition_link("main.tex", 5, 8).await.unwrap();
-
- test_bed.shutdown().await;
-
- assert_eq!(actual_links.len(), 1);
- let link = actual_links.pop().unwrap();
- verify_origin_selection_range(&link, 5, 5, 5, 11);
- assert_eq!(link.target_uri, test_bed.uri("main.tex").into());
- assert_eq!(link.target_range, Range::new_simple(0, 0, 3, 14));
- assert_eq!(link.target_selection_range, Range::new_simple(1, 0, 1, 14));
-}
-
-#[tokio::test]
-async fn latex_label_float_link() {
- let mut test_bed = TestBedBuilder::new()
- .file(
- "main.tex",
- indoc!(
- r#"
- \begin{figure}
- Foo
- \caption{Bar}
- \label{fig}
- \end{figure}
- %
- \ref{fig}
- "#
- ),
- )
- .build()
- .await;
- test_bed.spawn();
- test_bed
- .initialize(LOCATION_LINK_CAPABILITIES.clone())
- .await;
- test_bed.open("main.tex").await;
-
- let mut actual_links = test_bed.definition_link("main.tex", 6, 6).await.unwrap();
-
- test_bed.shutdown().await;
-
- assert_eq!(actual_links.len(), 1);
- let link = actual_links.pop().unwrap();
- verify_origin_selection_range(&link, 6, 5, 6, 8);
- assert_eq!(link.target_uri, test_bed.uri("main.tex").into());
- assert_eq!(link.target_range, Range::new_simple(0, 0, 4, 12));
- assert_eq!(link.target_selection_range, Range::new_simple(3, 0, 3, 11));
-}
-
-#[tokio::test]
-async fn latex_label_item_link() {
- let mut test_bed = TestBedBuilder::new()
- .file(
- "main.tex",
- indoc!(
- r#"
- \begin{enumerate}
- \item Foo
- \item\label{bar} Bar
- \item Baz
- \end{enumerate}
- %
- \ref{bar}
- "#
- ),
- )
- .build()
- .await;
- test_bed.spawn();
- test_bed
- .initialize(LOCATION_LINK_CAPABILITIES.clone())
- .await;
- test_bed.open("main.tex").await;
-
- let mut actual_links = test_bed.definition_link("main.tex", 6, 6).await.unwrap();
-
- test_bed.shutdown().await;
-
- assert_eq!(actual_links.len(), 1);
- let link = actual_links.pop().unwrap();
- verify_origin_selection_range(&link, 6, 5, 6, 8);
- assert_eq!(link.target_uri, test_bed.uri("main.tex").into());
- assert_eq!(link.target_range, Range::new_simple(0, 0, 4, 15));
- assert_eq!(link.target_selection_range, Range::new_simple(2, 9, 2, 20));
-}
-
-#[tokio::test]
-async fn latex_label_section_link() {
- let mut test_bed = TestBedBuilder::new()
- .file(
- "main.tex",
- indoc!(
- r#"
- \section{Foo}
- \label{sec:foo}
- %
- \section{Bar}
- \label{sec:bar}
- %
- \ref{sec:foo}
- "#
- ),
- )
- .build()
- .await;
- test_bed.spawn();
- test_bed
- .initialize(LOCATION_LINK_CAPABILITIES.clone())
- .await;
- test_bed.open("main.tex").await;
-
- let mut actual_links = test_bed.definition_link("main.tex", 6, 6).await.unwrap();
-
- test_bed.shutdown().await;
-
- assert_eq!(actual_links.len(), 1);
- let link = actual_links.pop().unwrap();
- verify_origin_selection_range(&link, 6, 5, 6, 12);
- assert_eq!(link.target_uri, test_bed.uri("main.tex").into());
- assert_eq!(link.target_range, Range::new_simple(0, 0, 3, 0));
- assert_eq!(link.target_selection_range, Range::new_simple(1, 0, 1, 15));
-}
-
-#[tokio::test]
-async fn latex_label_theorem_link() {
- let mut test_bed = TestBedBuilder::new()
- .file(
- "main.tex",
- indoc!(
- r#"
- \usepackage{amsthm}
- \newtheorem{lemma}{Lemma}
- %
- \begin{lemma}%
- \label{thm:foo}
- Foo
- \end{lemma}
- %
- \ref{thm:foo}
- "#
- ),
- )
- .build()
- .await;
- test_bed.spawn();
- test_bed
- .initialize(LOCATION_LINK_CAPABILITIES.clone())
- .await;
- test_bed.open("main.tex").await;
-
- let mut actual_links = test_bed.definition_link("main.tex", 8, 7).await.unwrap();
-
- test_bed.shutdown().await;
-
- assert_eq!(actual_links.len(), 1);
- let link = actual_links.pop().unwrap();
- verify_origin_selection_range(&link, 8, 5, 8, 12);
- assert_eq!(link.target_uri, test_bed.uri("main.tex").into());
- assert_eq!(link.target_range, Range::new_simple(3, 0, 6, 11));
- assert_eq!(link.target_selection_range, Range::new_simple(4, 0, 4, 15));
-}
diff --git a/support/texlab/tests/integration/document_symbol.rs b/support/texlab/tests/integration/document_symbol.rs
index afc3d54831..dbdbe64972 100644
--- a/support/texlab/tests/integration/document_symbol.rs
+++ b/support/texlab/tests/integration/document_symbol.rs
@@ -1,424 +1,438 @@
-use indoc::indoc;
-use texlab::{
- protocol::{DocumentSymbol, Range, RangeExt},
- test::{TestBedBuilder, NESTED_SYMBOL_CAPABILITIES},
+use anyhow::Result;
+use insta::assert_json_snapshot;
+use lsp_types::{
+ ClientCapabilities, DocumentSymbolClientCapabilities, TextDocumentClientCapabilities, Url,
};
-fn verify_symbol(
- symbol: &DocumentSymbol,
- name: &str,
- detail: Option<&str>,
- selection_range: Range,
- range: Range,
-) {
- assert_eq!(symbol.name, name);
- assert_eq!(symbol.detail.as_ref().map(AsRef::as_ref), detail);
- assert_eq!(symbol.selection_range, selection_range);
- assert_eq!(symbol.range, range);
+use crate::common::ServerTester;
+
+fn nested_symbol_capabilities() -> ClientCapabilities {
+ ClientCapabilities {
+ text_document: Some(TextDocumentClientCapabilities {
+ document_symbol: Some(DocumentSymbolClientCapabilities {
+ hierarchical_document_symbol_support: Some(true),
+ ..DocumentSymbolClientCapabilities::default()
+ }),
+ ..TextDocumentClientCapabilities::default()
+ }),
+ ..ClientCapabilities::default()
+ }
}
-#[tokio::test]
-async fn enumerate() {
- let mut test_bed = TestBedBuilder::new()
- .file(
- "main.tex",
- indoc!(
- r#"
- \documentclass{article}
- %
- \begin{document}
- %
- \begin{enumerate}
- \item\label{it:foo} Foo
- \item\label{it:bar} Bar
- \item[Baz] Baz
- \item[Qux]\label{it:qux} Qux
- \end{enumerate}
- %
- \end{document}
- "#
- ),
- )
- .file(
- "main.aux",
- indoc!(
- r#"
- \relax
- \newlabel{it:foo}{{1}{1}}
- \newlabel{it:qux}{{2}{1}}
- "#
- ),
- )
- .build()
- .await;
-
- test_bed.spawn();
- test_bed
- .initialize(NESTED_SYMBOL_CAPABILITIES.clone())
- .await;
- test_bed.open("main.tex").await;
- test_bed.open("main.aux").await;
-
- let mut actual_symbols = test_bed.document_symbol_nested("main.tex").await.unwrap();
-
- test_bed.shutdown().await;
-
- assert_eq!(actual_symbols.len(), 1);
- verify_symbol(
- &actual_symbols[0],
- "Enumerate",
- None,
- Range::new_simple(4, 0, 9, 15),
- Range::new_simple(4, 0, 9, 15),
- );
-
- let children = actual_symbols[0].children.take().unwrap();
- assert_eq!(children.len(), 4);
- verify_symbol(
- &children[0],
- "1",
- Some("it:foo"),
- Range::new_simple(5, 9, 5, 23),
- Range::new_simple(5, 4, 6, 4),
- );
- verify_symbol(
- &children[1],
- "Item",
- Some("it:bar"),
- Range::new_simple(6, 9, 6, 23),
- Range::new_simple(6, 4, 7, 4),
- );
- verify_symbol(
- &children[2],
- "Baz",
- None,
- Range::new_simple(7, 4, 7, 14),
- Range::new_simple(7, 4, 8, 4),
- );
- verify_symbol(
- &children[3],
- "Qux",
- Some("it:qux"),
- Range::new_simple(8, 14, 8, 28),
- Range::new_simple(8, 4, 9, 0),
- );
+#[test]
+fn test_enumerate_nested() -> Result<()> {
+ let server = ServerTester::launch_new_instance()?;
+ server.initialize(nested_symbol_capabilities(), None)?;
+ let uri = Url::parse("http://www.example.com/main.tex")?;
+ server.open_memory(
+ uri.clone(),
+ r#"
+ \documentclass{article}
+
+ \begin{document}
+
+ \begin{enumerate}
+ \item\label{it:foo} Foo
+ \item\label{it:bar} Bar
+ \item[Baz] Baz
+ \item[Qux]\label{it:qux} Qux
+ \end{enumerate}
+
+ \end{document}
+ "#,
+ "latex",
+ )?;
+ server.open_memory(
+ Url::parse("http://www.example.com/main.aux")?,
+ r#"
+ \relax
+ \newlabel{it:foo}{{1}{1}}
+ \newlabel{it:qux}{{2}{1}}
+ "#,
+ "latex",
+ )?;
+ assert_json_snapshot!(server.find_document_symbols(uri)?);
+ Ok(())
}
-#[tokio::test]
-async fn equation() {
- let mut test_bed = TestBedBuilder::new()
- .file(
- "main.tex",
- indoc!(
- r#"
- \documentclass{article}
- %
- \begin{document}
- %
- \begin{equation}\label{eq:foo}
- Foo
- \end{equation}
- %
- \begin{equation}\label{eq:bar}
- Bar
- \end{equation}
- %
- \begin{equation}
- Baz
- \end{equation}
- %
- \end{document}
- "#
- ),
- )
- .file(
- "main.aux",
- indoc!(
- r#"
- \relax
- \newlabel{eq:foo}{{1}{1}}
- "#
- ),
- )
- .build()
- .await;
-
- test_bed.spawn();
- test_bed
- .initialize(NESTED_SYMBOL_CAPABILITIES.clone())
- .await;
- test_bed.open("main.tex").await;
- test_bed.open("main.aux").await;
-
- let actual_symbols = test_bed.document_symbol_nested("main.tex").await.unwrap();
-
- test_bed.shutdown().await;
-
- assert_eq!(actual_symbols.len(), 3);
- verify_symbol(
- &actual_symbols[0],
- "Equation (1)",
- Some("eq:foo"),
- Range::new_simple(4, 16, 4, 30),
- Range::new_simple(4, 0, 6, 14),
- );
- verify_symbol(
- &actual_symbols[1],
- "Equation",
- Some("eq:bar"),
- Range::new_simple(8, 16, 8, 30),
- Range::new_simple(8, 0, 10, 14),
- );
- verify_symbol(
- &actual_symbols[2],
- "Equation",
- None,
- Range::new_simple(12, 0, 14, 14),
- Range::new_simple(12, 0, 14, 14),
- );
+#[test]
+fn test_enumerate_flat() -> Result<()> {
+ let server = ServerTester::launch_new_instance()?;
+ server.initialize(ClientCapabilities::default(), None)?;
+ let uri = Url::parse("http://www.example.com/main.tex")?;
+ server.open_memory(
+ uri.clone(),
+ r#"
+ \documentclass{article}
+
+ \begin{document}
+
+ \begin{enumerate}
+ \item\label{it:foo} Foo
+ \item\label{it:bar} Bar
+ \item[Baz] Baz
+ \item[Qux]\label{it:qux} Qux
+ \end{enumerate}
+
+ \end{document}
+ "#,
+ "latex",
+ )?;
+ server.open_memory(
+ Url::parse("http://www.example.com/main.aux")?,
+ r#"
+ \relax
+ \newlabel{it:foo}{{1}{1}}
+ \newlabel{it:qux}{{2}{1}}
+ "#,
+ "latex",
+ )?;
+ assert_json_snapshot!(server.find_document_symbols(uri)?);
+ Ok(())
}
-#[tokio::test]
-async fn float() {
- let mut test_bed = TestBedBuilder::new()
- .file(
- "main.tex",
- indoc!(
- r#"
- \documentclass{article}
- %
- \begin{document}
- %
- \begin{figure}
- Foo
- \caption{Foo}\label{fig:foo}
- \end{figure}
- %
- \begin{figure}
- Bar
- \caption{Bar}\label{fig:bar}
- \end{figure}
- %
- \begin{figure}
- Baz
- \caption{Baz}
- \end{figure}
- %
- \begin{figure}
- Qux
- \end{figure}
- %
- \end{document}
- "#
- ),
- )
- .file(
- "main.aux",
- indoc!(
- r#"
- \relax
- \@writefile{lof}{\contentsline {figure}{\numberline {1}{\ignorespaces Foo}}{1}\protected@file@percent }
- \newlabel{fig:foo}{{1}{1}}
- \@writefile{lof}{\contentsline {figure}{\numberline {2}{\ignorespaces Bar}}{1}\protected@file@percent }
- \@writefile{lof}{\contentsline {figure}{\numberline {3}{\ignorespaces Baz}}{1}\protected@file@percent }
- "#
- ),
- )
- .build()
- .await;
-
- test_bed.spawn();
- test_bed
- .initialize(NESTED_SYMBOL_CAPABILITIES.clone())
- .await;
- test_bed.open("main.tex").await;
- test_bed.open("main.aux").await;
-
- let actual_symbols = test_bed.document_symbol_nested("main.tex").await.unwrap();
-
- test_bed.shutdown().await;
-
- assert_eq!(actual_symbols.len(), 3);
- verify_symbol(
- &actual_symbols[0],
- "Figure 1: Foo",
- Some("fig:foo"),
- Range::new_simple(6, 17, 6, 32),
- Range::new_simple(4, 0, 7, 12),
- );
- verify_symbol(
- &actual_symbols[1],
- "Figure: Bar",
- Some("fig:bar"),
- Range::new_simple(11, 17, 11, 32),
- Range::new_simple(9, 0, 12, 12),
- );
- verify_symbol(
- &actual_symbols[2],
- "Figure: Baz",
- None,
- Range::new_simple(14, 0, 17, 12),
- Range::new_simple(14, 0, 17, 12),
- );
+#[test]
+fn test_equation_nested() -> Result<()> {
+ let server = ServerTester::launch_new_instance()?;
+ server.initialize(nested_symbol_capabilities(), None)?;
+ let uri = Url::parse("http://www.example.com/main.tex")?;
+ server.open_memory(
+ uri.clone(),
+ r#"
+ \documentclass{article}
+
+ \begin{document}
+
+ \begin{equation}\label{eq:foo}
+ Foo
+ \end{equation}
+
+ \begin{equation}\label{eq:bar}
+ Bar
+ \end{equation}
+
+ \begin{equation}
+ Baz
+ \end{equation}
+
+ \end{document}
+ "#,
+ "latex",
+ )?;
+ server.open_memory(
+ Url::parse("http://www.example.com/main.aux")?,
+ r#"
+ \relax
+ \newlabel{eq:foo}{{1}{1}}
+ "#,
+ "latex",
+ )?;
+ assert_json_snapshot!(server.find_document_symbols(uri)?);
+ Ok(())
}
-#[tokio::test]
-async fn section() {
- let mut test_bed = TestBedBuilder::new()
- .file(
- "main.tex",
- indoc!(
- r#"
- \documentclass{article}
- %
- \begin{document}
- %
- \section{Foo}
- %
- \section{Bar}\label{sec:bar}
- %
- \subsection{Baz}\label{sec:baz}
- %
- \end{document}
- "#
- ),
- )
- .file(
- "main.aux",
- indoc!(
- r#"
- \relax
- \@writefile{toc}{\contentsline {section}{\numberline {1}Foo}{1}\protected@file@percent }
- \@writefile{toc}{\contentsline {section}{\numberline {2}Bar}{1}\protected@file@percent }
- \newlabel{sec:bar}{{2}{1}}
- "#
- ),
- )
- .build()
- .await;
-
- test_bed.spawn();
- test_bed
- .initialize(NESTED_SYMBOL_CAPABILITIES.clone())
- .await;
- test_bed.open("main.tex").await;
- test_bed.open("main.aux").await;
-
- let mut actual_symbols = test_bed.document_symbol_nested("main.tex").await.unwrap();
-
- test_bed.shutdown().await;
-
- assert_eq!(actual_symbols.len(), 2);
- verify_symbol(
- &actual_symbols[0],
- "Foo",
- None,
- Range::new_simple(4, 0, 4, 13),
- Range::new_simple(4, 0, 6, 0),
- );
- verify_symbol(
- &actual_symbols[1],
- "2 Bar",
- Some("sec:bar"),
- Range::new_simple(6, 0, 6, 13),
- Range::new_simple(6, 0, 10, 0),
- );
-
- let children = actual_symbols[1].children.take().unwrap();
- assert_eq!(children.len(), 1);
- verify_symbol(
- &children[0],
- "Baz",
- Some("sec:baz"),
- Range::new_simple(8, 0, 8, 16),
- Range::new_simple(8, 0, 10, 0),
- );
+#[test]
+fn test_equation_flat() -> Result<()> {
+ let server = ServerTester::launch_new_instance()?;
+ server.initialize(ClientCapabilities::default(), None)?;
+ let uri = Url::parse("http://www.example.com/main.tex")?;
+ server.open_memory(
+ uri.clone(),
+ r#"
+ \documentclass{article}
+
+ \begin{document}
+
+ \begin{equation}\label{eq:foo}
+ Foo
+ \end{equation}
+
+ \begin{equation}\label{eq:bar}
+ Bar
+ \end{equation}
+
+ \begin{equation}
+ Baz
+ \end{equation}
+
+ \end{document}
+ "#,
+ "latex",
+ )?;
+ server.open_memory(
+ Url::parse("http://www.example.com/main.aux")?,
+ r#"
+ \relax
+ \newlabel{eq:foo}{{1}{1}}
+ "#,
+ "latex",
+ )?;
+ assert_json_snapshot!(server.find_document_symbols(uri)?);
+ Ok(())
}
-#[tokio::test]
-async fn theorem() {
- let mut test_bed = TestBedBuilder::new()
- .file(
- "main.tex",
- indoc!(
- r#"
- \documentclass{article}
- \usepackage{amsthm}
- \newtheorem{lemma}{Lemma}
- %
- \begin{document}
- %
- \begin{lemma}[Foo]\label{thm:foo}
- Foo
- \end{lemma}
- %
- \begin{lemma}\label{thm:bar}
- Bar
- \end{lemma}
- %
- \begin{lemma}\label{thm:baz}
- Baz
- \end{lemma}
- %
- \begin{lemma}[Qux]
- Qux
- \end{lemma}
- %
- \end{document}
- "#
- ),
- )
- .file(
- "main.aux",
- indoc!(
- r#"
- \relax
- \newlabel{thm:foo}{{1}{1}}
- \newlabel{thm:bar}{{2}{1}}
- "#
- ),
- )
- .build()
- .await;
-
- test_bed.spawn();
- test_bed
- .initialize(NESTED_SYMBOL_CAPABILITIES.clone())
- .await;
- test_bed.open("main.tex").await;
- test_bed.open("main.aux").await;
-
- let actual_symbols = test_bed.document_symbol_nested("main.tex").await.unwrap();
-
- test_bed.shutdown().await;
-
- assert_eq!(actual_symbols.len(), 4);
- verify_symbol(
- &actual_symbols[0],
- "Lemma 1 (Foo)",
- Some("thm:foo"),
- Range::new_simple(6, 18, 6, 33),
- Range::new_simple(6, 0, 8, 11),
- );
- verify_symbol(
- &actual_symbols[1],
- "Lemma 2",
- Some("thm:bar"),
- Range::new_simple(10, 13, 10, 28),
- Range::new_simple(10, 0, 12, 11),
- );
- verify_symbol(
- &actual_symbols[2],
- "Lemma",
- Some("thm:baz"),
- Range::new_simple(14, 13, 14, 28),
- Range::new_simple(14, 0, 16, 11),
- );
- verify_symbol(
- &actual_symbols[3],
- "Lemma (Qux)",
- None,
- Range::new_simple(18, 0, 20, 11),
- Range::new_simple(18, 0, 20, 11),
- );
+#[test]
+fn test_float_nested() -> Result<()> {
+ let server = ServerTester::launch_new_instance()?;
+ server.initialize(nested_symbol_capabilities(), None)?;
+ let uri = Url::parse("http://www.example.com/main.tex")?;
+ server.open_memory(
+ uri.clone(),
+ r#"
+ \documentclass{article}
+
+ \begin{document}
+
+ \begin{figure}
+ Foo
+ \caption{Foo}\label{fig:foo}
+ \end{figure}
+
+ \begin{figure}
+ Bar
+ \caption{Bar}\label{fig:bar}
+ \end{figure}
+
+ \begin{figure}
+ Baz
+ \caption{Baz}
+ \end{figure}
+
+ \begin{figure}
+ Qux
+ \end{figure}
+
+ \end{document}
+ "#,
+ "latex",
+ )?;
+ server.open_memory(
+ Url::parse("http://www.example.com/main.aux")?,
+ r#"
+ \relax
+ \@writefile{lof}{\contentsline {figure}{\numberline {1}{\ignorespaces Foo}}{1}\protected@file@percent }
+ \newlabel{fig:foo}{{1}{1}}
+ \@writefile{lof}{\contentsline {figure}{\numberline {2}{\ignorespaces Bar}}{1}\protected@file@percent }
+ \@writefile{lof}{\contentsline {figure}{\numberline {3}{\ignorespaces Baz}}{1}\protected@file@percent }
+ "#,
+ "latex",
+ )?;
+ assert_json_snapshot!(server.find_document_symbols(uri)?);
+ Ok(())
+}
+
+#[test]
+fn test_float_flat() -> Result<()> {
+ let server = ServerTester::launch_new_instance()?;
+ server.initialize(ClientCapabilities::default(), None)?;
+ let uri = Url::parse("http://www.example.com/main.tex")?;
+ server.open_memory(
+ uri.clone(),
+ r#"
+ \documentclass{article}
+
+ \begin{document}
+
+ \begin{figure}
+ Foo
+ \caption{Foo}\label{fig:foo}
+ \end{figure}
+
+ \begin{figure}
+ Bar
+ \caption{Bar}\label{fig:bar}
+ \end{figure}
+
+ \begin{figure}
+ Baz
+ \caption{Baz}
+ \end{figure}
+
+ \begin{figure}
+ Qux
+ \end{figure}
+
+ \end{document}
+ "#,
+ "latex",
+ )?;
+ server.open_memory(
+ Url::parse("http://www.example.com/main.aux")?,
+ r#"
+ \relax
+ \@writefile{lof}{\contentsline {figure}{\numberline {1}{\ignorespaces Foo}}{1}\protected@file@percent }
+ \newlabel{fig:foo}{{1}{1}}
+ \@writefile{lof}{\contentsline {figure}{\numberline {2}{\ignorespaces Bar}}{1}\protected@file@percent }
+ \@writefile{lof}{\contentsline {figure}{\numberline {3}{\ignorespaces Baz}}{1}\protected@file@percent }
+ "#,
+ "latex",
+ )?;
+ assert_json_snapshot!(server.find_document_symbols(uri)?);
+ Ok(())
+}
+
+#[test]
+fn test_section_nested() -> Result<()> {
+ let server = ServerTester::launch_new_instance()?;
+ server.initialize(nested_symbol_capabilities(), None)?;
+ let uri = Url::parse("http://www.example.com/main.tex")?;
+ server.open_memory(
+ uri.clone(),
+ r#"
+ \documentclass{article}
+
+ \begin{document}
+
+ \section{Foo}
+
+ \section{Bar}\label{sec:bar}
+
+ \subsection{Baz}\label{sec:baz}
+
+ \end{document}
+ "#,
+ "latex",
+ )?;
+ server.open_memory(
+ Url::parse("http://www.example.com/main.aux")?,
+ r#"
+ \relax
+ \@writefile{toc}{\contentsline {section}{\numberline {1}Foo}{1}\protected@file@percent }
+ \@writefile{toc}{\contentsline {section}{\numberline {2}Bar}{1}\protected@file@percent }
+ \newlabel{sec:bar}{{2}{1}}
+ "#,
+ "latex",
+ )?;
+ assert_json_snapshot!(server.find_document_symbols(uri)?);
+ Ok(())
+}
+
+#[test]
+fn test_section_flat() -> Result<()> {
+ let server = ServerTester::launch_new_instance()?;
+ server.initialize(ClientCapabilities::default(), None)?;
+ let uri = Url::parse("http://www.example.com/main.tex")?;
+ server.open_memory(
+ uri.clone(),
+ r#"
+ \documentclass{article}
+
+ \begin{document}
+
+ \section{Foo}
+
+ \section{Bar}\label{sec:bar}
+
+ \subsection{Baz}\label{sec:baz}
+
+ \end{document}
+ "#,
+ "latex",
+ )?;
+ server.open_memory(
+ Url::parse("http://www.example.com/main.aux")?,
+ r#"
+ \relax
+ \@writefile{toc}{\contentsline {section}{\numberline {1}Foo}{1}\protected@file@percent }
+ \@writefile{toc}{\contentsline {section}{\numberline {2}Bar}{1}\protected@file@percent }
+ \newlabel{sec:bar}{{2}{1}}
+ "#,
+ "latex",
+ )?;
+ assert_json_snapshot!(server.find_document_symbols(uri)?);
+ Ok(())
+}
+
+#[test]
+fn test_theorem_nested() -> Result<()> {
+ let server = ServerTester::launch_new_instance()?;
+ server.initialize(nested_symbol_capabilities(), None)?;
+ let uri = Url::parse("http://www.example.com/main.tex")?;
+ server.open_memory(
+ uri.clone(),
+ r#"
+ \documentclass{article}
+ \usepackage{amsthm}
+ \newtheorem{lemma}{Lemma}
+
+ \begin{document}
+
+ \begin{lemma}[Foo]\label{thm:foo}
+ Foo
+ \end{lemma}
+
+ \begin{lemma}\label{thm:bar}
+ Bar
+ \end{lemma}
+
+ \begin{lemma}\label{thm:baz}
+ Baz
+ \end{lemma}
+
+ \begin{lemma}[Qux]
+ Qux
+ \end{lemma}
+
+ \end{document}
+ "#,
+ "latex",
+ )?;
+ server.open_memory(
+ Url::parse("http://www.example.com/main.aux")?,
+ r#"
+ \relax
+ \newlabel{thm:foo}{{1}{1}}
+ \newlabel{thm:bar}{{2}{1}}
+ "#,
+ "latex",
+ )?;
+ assert_json_snapshot!(server.find_document_symbols(uri)?);
+ Ok(())
+}
+
+#[test]
+fn test_theorem_flat() -> Result<()> {
+ let server = ServerTester::launch_new_instance()?;
+ server.initialize(ClientCapabilities::default(), None)?;
+ let uri = Url::parse("http://www.example.com/main.tex")?;
+ server.open_memory(
+ uri.clone(),
+ r#"
+ \documentclass{article}
+ \usepackage{amsthm}
+ \newtheorem{lemma}{Lemma}
+
+ \begin{document}
+
+ \begin{lemma}[Foo]\label{thm:foo}
+ Foo
+ \end{lemma}
+
+ \begin{lemma}\label{thm:bar}
+ Bar
+ \end{lemma}
+
+ \begin{lemma}\label{thm:baz}
+ Baz
+ \end{lemma}
+
+ \begin{lemma}[Qux]
+ Qux
+ \end{lemma}
+
+ \end{document}
+ "#,
+ "latex",
+ )?;
+ server.open_memory(
+ Url::parse("http://www.example.com/main.aux")?,
+ r#"
+ \relax
+ \newlabel{thm:foo}{{1}{1}}
+ \newlabel{thm:bar}{{2}{1}}
+ "#,
+ "latex",
+ )?;
+ assert_json_snapshot!(server.find_document_symbols(uri)?);
+ Ok(())
}
diff --git a/support/texlab/tests/integration/folding.rs b/support/texlab/tests/integration/folding.rs
deleted file mode 100644
index 3540e2c680..0000000000
--- a/support/texlab/tests/integration/folding.rs
+++ /dev/null
@@ -1,126 +0,0 @@
-use indoc::indoc;
-use texlab::{
- protocol::{FoldingRange, FoldingRangeKind},
- test::{TestBedBuilder, PULL_CAPABILITIES},
-};
-
-#[tokio::test]
-async fn empty_latex_document() {
- let mut test_bed = TestBedBuilder::new().file("main.tex", "").build().await;
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
- test_bed.open("main.tex").await;
-
- let actual_foldings = test_bed.folding_range("main.tex").await.unwrap();
-
- test_bed.shutdown().await;
-
- assert!(actual_foldings.is_empty());
-}
-
-#[tokio::test]
-async fn empty_bibtex_document() {
- let mut test_bed = TestBedBuilder::new().file("main.bib", "").build().await;
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
- test_bed.open("main.bib").await;
-
- let actual_foldings = test_bed.folding_range("main.bib").await.unwrap();
-
- test_bed.shutdown().await;
-
- assert!(actual_foldings.is_empty());
-}
-
-#[tokio::test]
-async fn latex_sections_with_env() {
- let mut test_bed = TestBedBuilder::new()
- .file(
- "main.tex",
- indoc!(
- r#"
- \begin{document}
- \section{Foo}
- Foo
- \section{Bar}
- Bar
- \end{document}
- "#
- ),
- )
- .build()
- .await;
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
- test_bed.open("main.tex").await;
-
- let actual_foldings = test_bed.folding_range("main.tex").await.unwrap();
-
- test_bed.shutdown().await;
-
- let expected_foldings = vec![
- FoldingRange {
- start_line: 0,
- start_character: Some(16),
- end_line: 5,
- end_character: Some(0),
- kind: Some(FoldingRangeKind::Region),
- },
- FoldingRange {
- start_line: 1,
- start_character: Some(13),
- end_line: 2,
- end_character: Some(0),
- kind: Some(FoldingRangeKind::Region),
- },
- ];
- assert_eq!(actual_foldings, expected_foldings);
-}
-
-#[tokio::test]
-async fn bibtex_entry() {
- let mut test_bed = TestBedBuilder::new()
- .file(
- "main.bib",
- indoc!(
- r#"
- @article{foo,
- author = {Foo Bar},
- title = {Baz Qux},
- }
- "#
- ),
- )
- .build()
- .await;
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
- test_bed.open("main.bib").await;
-
- let actual_foldings = test_bed.folding_range("main.bib").await.unwrap();
-
- test_bed.shutdown().await;
-
- let expected_foldings = vec![FoldingRange {
- start_line: 0,
- start_character: Some(0),
- end_line: 3,
- end_character: Some(1),
- kind: Some(FoldingRangeKind::Region),
- }];
- assert_eq!(actual_foldings, expected_foldings);
-}
-
-#[tokio::test]
-async fn unknown_file() {
- let mut test_bed = TestBedBuilder::new().build().await;
-
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
-
- let actual_foldings = test_bed.folding_range("main.tex").await;
-
- test_bed.shutdown().await;
-
- assert_eq!(actual_foldings, None);
-}
diff --git a/support/texlab/tests/integration/highlight.rs b/support/texlab/tests/integration/highlight.rs
deleted file mode 100644
index ba77a7b8e3..0000000000
--- a/support/texlab/tests/integration/highlight.rs
+++ /dev/null
@@ -1,83 +0,0 @@
-use indoc::indoc;
-use texlab::{
- protocol::{DocumentHighlight, DocumentHighlightKind, Range, RangeExt},
- test::{TestBedBuilder, PULL_CAPABILITIES},
-};
-
-#[tokio::test]
-async fn empty_latex_document() {
- let mut test_bed = TestBedBuilder::new().file("main.tex", "").build().await;
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
- test_bed.open("main.tex").await;
-
- let actual_highlights = test_bed.document_highlight("main.tex", 0, 0).await.unwrap();
-
- test_bed.shutdown().await;
-
- assert!(actual_highlights.is_empty());
-}
-
-#[tokio::test]
-async fn empty_bibtex_document() {
- let mut test_bed = TestBedBuilder::new().file("main.bib", "").build().await;
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
- test_bed.open("main.bib").await;
-
- let actual_highlights = test_bed.document_highlight("main.bib", 0, 0).await.unwrap();
-
- test_bed.shutdown().await;
-
- assert!(actual_highlights.is_empty());
-}
-
-#[tokio::test]
-async fn label() {
- let mut test_bed = TestBedBuilder::new()
- .file(
- "main.tex",
- indoc!(
- r#"
- \label{foo}
- \ref{foo}
- "#
- ),
- )
- .build()
- .await;
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
- test_bed.open("main.tex").await;
-
- let actual_highlights = test_bed.document_highlight("main.tex", 0, 7).await.unwrap();
-
- let expected_highlights = vec![
- DocumentHighlight {
- range: Range::new_simple(0, 7, 0, 10),
- kind: Some(DocumentHighlightKind::Write),
- },
- DocumentHighlight {
- range: Range::new_simple(1, 5, 1, 8),
- kind: Some(DocumentHighlightKind::Read),
- },
- ];
-
- test_bed.shutdown().await;
-
- assert_eq!(actual_highlights, expected_highlights);
-}
-
-#[tokio::test]
-async fn unknown_file() {
- let mut test_bed = TestBedBuilder::new().build().await;
-
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
-
- let actual_highlights = test_bed.document_highlight("main.tex", 0, 0).await;
-
- test_bed.shutdown().await;
-
- assert_eq!(actual_highlights, None);
-}
diff --git a/support/texlab/tests/integration/hover.rs b/support/texlab/tests/integration/hover.rs
index 319ebcc5e9..70b14cccd0 100644
--- a/support/texlab/tests/integration/hover.rs
+++ b/support/texlab/tests/integration/hover.rs
@@ -1,86 +1,89 @@
-use indoc::indoc;
-use texlab::{
- protocol::{HoverContents, MarkupContent, MarkupKind},
- test::{TestBedBuilder, PULL_CAPABILITIES},
-};
+use anyhow::Result;
+use insta::assert_json_snapshot;
+use lsp_types::ClientCapabilities;
-#[tokio::test]
-async fn label_theorem_child_file() {
- let mut test_bed = TestBedBuilder::new()
- .file(
- "main.tex",
- indoc!(
- r#"
- \documentclass{article}
- \newtheorem{lemma}{Lemma}
- \include{child}
- \ref{thm:foo}
- "#
- ),
- )
- .file(
- "child.tex",
- indoc!(
- r#"
- \begin{lemma}\label{thm:foo}
- 1 + 1 = 2
- \end{lemma}
- "#
- ),
- )
- .build()
- .await;
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
- test_bed.open("main.tex").await;
+use crate::common::ServerTester;
- let actual_hover = test_bed.hover("main.tex", 3, 8).await.unwrap().unwrap();
- assert_eq!(
- actual_hover.contents,
- HoverContents::Markup(MarkupContent {
- kind: MarkupKind::PlainText,
- value: "Lemma".into()
- })
- );
+#[test]
+fn test_empty_bibtex_document() -> Result<()> {
+ let server = ServerTester::launch_new_instance()?;
+ server.initialize(ClientCapabilities::default(), None)?;
+ let uri = server.open("main.bib", "", "bibtex", false)?;
+ assert_json_snapshot!(server.hover(uri, 0, 0)?);
+ Ok(())
}
-#[tokio::test]
-async fn label_theorem_child_file_number() {
- let mut test_bed = TestBedBuilder::new()
- .file(
- "main.tex",
- indoc!(
- r#"
- \documentclass{article}
- \newtheorem{lemma}{Lemma}
- \include{child}
- \ref{thm:foo}
- "#
- ),
- )
- .file(
- "child.tex",
- indoc!(
- r#"
- \begin{lemma}[Foo]\label{thm:foo}
- 1 + 1 = 2
- \end{lemma}
- "#
- ),
- )
- .file("child.aux", r#"\newlabel{thm:foo}{{1}{1}{Foo}{lemma.1}{}}"#)
- .build()
- .await;
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
- test_bed.open("main.tex").await;
+#[test]
+fn test_empty_labtex_document() -> Result<()> {
+ let server = ServerTester::launch_new_instance()?;
+ server.initialize(ClientCapabilities::default(), None)?;
+ let uri = server.open("main.tex", "", "latex", false)?;
+ assert_json_snapshot!(server.hover(uri, 0, 0)?);
+ Ok(())
+}
+
+#[test]
+fn test_label_theorem_child_file() -> Result<()> {
+ let server = ServerTester::launch_new_instance()?;
+ server.initialize(ClientCapabilities::default(), None)?;
+ let uri = server.open(
+ "main.tex",
+ r#"
+ \documentclass{article}
+ \newtheorem{lemma}{Lemma}
+ \include{child}
+ \ref{thm:foo}
+ "#,
+ "latex",
+ false,
+ )?;
+ server.open(
+ "child.tex",
+ r#"
+ \begin{lemma}\label{thm:foo}
+ 1 + 1 = 2
+ \end{lemma}
+ "#,
+ "latex",
+ false,
+ )?;
+
+ assert_json_snapshot!(server.hover(uri, 3, 8)?);
+ Ok(())
+}
+
+#[test]
+fn test_label_theorem_child_file_mumber() -> Result<()> {
+ let server = ServerTester::launch_new_instance()?;
+ server.initialize(ClientCapabilities::default(), None)?;
+ let uri = server.open(
+ "main.tex",
+ r#"
+ \documentclass{article}
+ \newtheorem{lemma}{Lemma}
+ \include{child}
+ \ref{thm:foo}
+ "#,
+ "latex",
+ false,
+ )?;
+ server.open(
+ "child.tex",
+ r#"
+ \begin{lemma}[Foo]\label{thm:foo}
+ 1 + 1 = 2
+ \end{lemma}
+ "#,
+ "latex",
+ false,
+ )?;
+ server.open(
+ "child.aux",
+ r#"\newlabel{thm:foo}{{1}{1}{Foo}{lemma.1}{}}"#,
+ "latex",
+ false,
+ )?;
- let actual_hover = test_bed.hover("main.tex", 3, 8).await.unwrap().unwrap();
- assert_eq!(
- actual_hover.contents,
- HoverContents::Markup(MarkupContent {
- kind: MarkupKind::PlainText,
- value: "Lemma 1 (Foo)".into()
- })
- );
+ assert_json_snapshot!(server.hover(uri, 3, 8)?);
+ Ok(())
}
diff --git a/support/texlab/tests/integration/issues.rs b/support/texlab/tests/integration/issues.rs
index 59a1bfeacb..5d6caeb79d 100644
--- a/support/texlab/tests/integration/issues.rs
+++ b/support/texlab/tests/integration/issues.rs
@@ -1,350 +1,41 @@
-use indoc::indoc;
-use texlab::{
- protocol::{HoverContents, Location, LocationLink, MarkupContent, MarkupKind, Range, RangeExt},
- test::{TestBedBuilder, LOCATION_LINK_CAPABILITIES, PULL_CAPABILITIES},
-};
-
-#[tokio::test]
-async fn issue_14() {
- let mut test_bed = TestBedBuilder::new()
- .file("main.tex", r#"\(\be\)"#)
- .build()
- .await;
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
- test_bed.open("main.tex").await;
-
- let actual_items = test_bed.completion("main.tex", 0, 5).await.unwrap();
-
- test_bed.shutdown().await;
- assert!(actual_items.iter().any(|item| item.label == "beta"));
-}
-
-#[tokio::test]
-async fn issue_15_link() {
- let mut test_bed = TestBedBuilder::new()
- .file(
- "main.tex",
- indoc!(
- r#"
- \documentclass{article}
- \begin{document}
- \newcommand{\test}{test}
- hello \test{}
- \end{document}
- "#
- ),
- )
- .build()
- .await;
- test_bed.spawn();
- test_bed
- .initialize(LOCATION_LINK_CAPABILITIES.clone())
- .await;
- test_bed.open("main.tex").await;
-
- let actual_links = test_bed.definition_link("main.tex", 3, 9).await.unwrap();
-
- test_bed.shutdown().await;
- let expected_links = vec![LocationLink {
- origin_selection_range: Some(Range::new_simple(3, 6, 3, 13)),
- target_range: Range::new_simple(2, 0, 2, 24),
- target_selection_range: Range::new_simple(2, 0, 2, 24),
- target_uri: test_bed.uri("main.tex").into(),
- }];
- assert_eq!(actual_links, expected_links);
-}
-
-#[tokio::test]
-async fn issue_15_location() {
- let mut test_bed = TestBedBuilder::new()
- .file(
- "main.tex",
- indoc!(
- r#"
- \documentclass{article}
- \begin{document}
- \newcommand{\test}{test}
- hello \test{}
- \end{document}
- "#
- ),
- )
- .build()
- .await;
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
- test_bed.open("main.tex").await;
-
- let actual_locations = test_bed
- .definition_location("main.tex", 3, 9)
- .await
- .unwrap();
-
- test_bed.shutdown().await;
- let expected_locations = vec![Location {
- range: Range::new_simple(2, 0, 2, 24),
- uri: test_bed.uri("main.tex").into(),
- }];
- assert_eq!(actual_locations, expected_locations);
-}
-
-#[tokio::test]
-async fn issue_17() {
- let mut test_bed = TestBedBuilder::new()
- .file("main.bib", r#"@ARTICLE{foo,}"#)
- .build()
- .await;
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
- test_bed.open("main.bib").await;
-
- let actual_hover = test_bed.hover("main.bib", 0, 3).await.unwrap().unwrap();
-
- test_bed.shutdown().await;
- assert_eq!(actual_hover.range.unwrap(), Range::new_simple(0, 0, 0, 8));
-}
-
-#[tokio::test]
-async fn issue_21() {
- let mut test_bed = TestBedBuilder::new()
- .file(
- "main.tex",
- indoc!(
- r#"
- \label{foo}
- \cref{}
- "#
- ),
- )
- .build()
- .await;
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
- test_bed.open("main.tex").await;
-
- let actual_items = test_bed.completion("main.tex", 1, 6).await.unwrap();
-
- test_bed.shutdown().await;
- assert!(actual_items.iter().any(|item| item.label == "foo"));
-}
-
-#[tokio::test]
-async fn issue_22_include() {
- let mut test_bed = TestBedBuilder::new()
- .file(
- "main.tex",
- indoc!(
- r#"
- \bibliography{bibfile}
- \cite{}
- "#
- ),
- )
- .file("bibfile.bib", r#"@article{foo,}"#)
- .build()
- .await;
-
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
- test_bed.open("main.tex").await;
-
- let actual_items = test_bed.completion("main.tex", 1, 6).await.unwrap();
-
- test_bed.shutdown().await;
- assert!(actual_items.iter().any(|item| item.label == "foo"));
-}
-
-#[tokio::test]
-async fn issue_22_definition() {
- let mut test_bed = TestBedBuilder::new()
- .file(
- "main.tex",
- indoc!(
- r#"
- \bibliography{bibfile}
- \cite{A,B}
- "#
- ),
- )
- .file(
- "bibfile.bib",
- indoc!(
- r#"
- @article{A,}
- @article{B,}
- "#
- ),
- )
- .build()
- .await;
-
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
- test_bed.open("main.tex").await;
-
- let actual_locations = test_bed
- .definition_location("main.tex", 1, 8)
- .await
- .unwrap();
-
- test_bed.shutdown().await;
- let expected_locations = vec![Location {
- range: Range::new_simple(1, 9, 1, 10),
- uri: test_bed.uri("bibfile.bib").into(),
- }];
- assert_eq!(actual_locations, expected_locations);
-}
-
-#[tokio::test]
-async fn issue_23_completion() {
- let mut test_bed = TestBedBuilder::new()
- .file(
- "main.tex",
- indoc!(
- r#"
- \documentclass{article}
- \begin{document}
- \include{test1}
- \include{test2}
- \end{document}
- "#
- ),
- )
- .file("test1.tex", r#"\section{Section 1}\label{sec:1}"#)
- .file(
- "test2.tex",
- indoc!(
- r#"
- \section{Section 2}\label{sec:2}
- %
- This section continues from Section \ref{sec}"#
- ),
- )
- .build()
- .await;
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
- test_bed.open("test2.tex").await;
- test_bed.detect_root("test2.tex").await;
-
- let actual_items = test_bed.completion("test2.tex", 2, 42).await.unwrap();
-
- test_bed.shutdown().await;
- assert_eq!(actual_items.len(), 2);
-}
-
-#[tokio::test]
-async fn issue_23_rename() {
- let mut test_bed = TestBedBuilder::new()
- .file(
- "main.tex",
- indoc!(
- r#"
- \documentclass{article}
- \begin{document}
- \include{test1}
- \include{test2}
- \end{document}
- "#
- ),
- )
- .file("test1.tex", r#"\section{Section 1}\label{sec:1}"#)
- .file(
- "test2.tex",
- indoc!(
- r#"
- \section{Section 2}\label{sec:2}
- %
- This section continues from Section \ref{sec:1}"#
- ),
- )
- .build()
- .await;
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
- test_bed.open("test1.tex").await;
- test_bed.detect_root("test1.tex").await;
-
- let workspace_edit = test_bed
- .rename("test1.tex", 0, 27, "foo")
- .await
- .unwrap()
- .unwrap();
-
- test_bed.shutdown().await;
- let changes = workspace_edit.changes.unwrap();
- assert!(changes.contains_key(&test_bed.uri("test1.tex")));
- assert!(changes.contains_key(&test_bed.uri("test2.tex")));
-}
-
-#[tokio::test]
-async fn issue_23_hover() {
- let mut test_bed = TestBedBuilder::new()
- .file(
- "main.tex",
- indoc!(
- r#"
- \documentclass{article}
- \begin{document}
- \include{test1}
- \include{test2}
- \end{document}
- "#
- ),
- )
- .file("test1.tex", r#"\section{Section 1}\label{sec:1}"#)
- .file(
- "test2.tex",
- indoc!(
- r#"
- \section{Section 2}\label{sec:2}
- %
- This section continues from Section \ref{sec:1}"#
- ),
- )
- .build()
- .await;
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
- test_bed.open("test2.tex").await;
- test_bed.detect_root("test2.tex").await;
-
- let actual_hover = test_bed.hover("test2.tex", 2, 42).await.unwrap().unwrap();
-
- test_bed.shutdown().await;
- assert_eq!(
- actual_hover.contents,
- HoverContents::Markup(MarkupContent {
- kind: MarkupKind::PlainText,
- value: "Section (Section 1)".into()
- })
- );
-}
-
-#[tokio::test]
-async fn issue_26() {
- let mut test_bed = TestBedBuilder::new()
- .file(
- "main.tex",
- indoc!(
- r#"
- \section{Foo}\label{sec:foo}
- \begin{equation}\label{eq:bar}
- \end{equation}
- \eqref{}
- "#
- ),
- )
- .build()
- .await;
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
- test_bed.open("main.tex").await;
-
- let actual_items = test_bed.completion("main.tex", 3, 7).await.unwrap();
-
- test_bed.shutdown().await;
- assert!(actual_items.iter().any(|item| item.label == "eq:bar"));
- assert!(actual_items.iter().all(|item| item.label != "sec:foo"));
+use std::{fs, thread, time::Duration};
+
+use anyhow::Result;
+use lsp_types::ClientCapabilities;
+
+use crate::common::ServerTester;
+
+#[test]
+#[cfg(feature = "completion")]
+fn test_408_parent_expansion() -> Result<()> {
+ let server = ServerTester::launch_new_instance()?;
+ server.initialize(ClientCapabilities::default(), None)?;
+ let root = server.directory.path();
+
+ let level1 = root.join("level1");
+ let level2 = level1.join("level2");
+ let level3 = level2.join("level3");
+ fs::create_dir_all(&level3)?;
+ fs::write(level3.join("d.tex"), "d")?;
+ fs::write(
+ level2.join("c.tex"),
+ r#"\documentclass{subfiles}\begin{document}\include{level3/d}\end{document}"#,
+ )?;
+ fs::write(
+ level1.join("b.tex"),
+ r#"\documentclass{article}\begin{document}\include{level2/c}\end{document}"#,
+ )?;
+ fs::write(root.join("a.tex"), "}")?;
+
+ thread::sleep(Duration::from_millis(300));
+
+ let uri = server.open("level1/level2/level3/d.tex", "d", "latex", false)?;
+ server.complete(uri, 0, 0)?;
+
+ let diagnostics_by_uri = server.diagnostics_by_uri.lock().unwrap();
+ assert!(diagnostics_by_uri
+ .iter()
+ .all(|(uri, _)| !uri.as_str().ends_with("a.tex")));
+
+ Ok(())
}
diff --git a/support/texlab/tests/integration/link.rs b/support/texlab/tests/integration/link.rs
deleted file mode 100644
index 8db718bd26..0000000000
--- a/support/texlab/tests/integration/link.rs
+++ /dev/null
@@ -1,173 +0,0 @@
-use indoc::indoc;
-use texlab::{
- protocol::{DocumentLink, Range, RangeExt},
- test::{TestBedBuilder, PULL_CAPABILITIES, PUSH_CAPABILITIES},
-};
-
-#[tokio::test]
-async fn empty_latex_document() {
- let mut test_bed = TestBedBuilder::new().file("main.tex", "").build().await;
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
- test_bed.open("main.tex").await;
-
- let actual_links = test_bed.document_link("main.tex").await.unwrap();
-
- test_bed.shutdown().await;
-
- assert!(actual_links.is_empty());
-}
-
-#[tokio::test]
-async fn default_settings() {
- let mut test_bed = TestBedBuilder::new()
- .file(
- "main.tex",
- indoc!(
- r#"
- \include{foo/bar}
- \input{qux.tex}
- "#
- ),
- )
- .file("foo/bar.tex", "")
- .file("qux.tex", "")
- .build()
- .await;
-
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
- test_bed.open("main.tex").await;
-
- let actual_links = test_bed.document_link("main.tex").await.unwrap();
-
- test_bed.shutdown().await;
-
- let expected_links = vec![
- DocumentLink {
- range: Range::new_simple(0, 9, 0, 16),
- target: test_bed.uri("foo/bar.tex").into(),
- tooltip: None,
- },
- DocumentLink {
- range: Range::new_simple(1, 7, 1, 14),
- target: test_bed.uri("qux.tex").into(),
- tooltip: None,
- },
- ];
- assert_eq!(actual_links, expected_links);
-}
-
-#[tokio::test]
-async fn root_directory() {
- let mut test_bed = TestBedBuilder::new()
- .file("src/main.tex", r#"\include{src/foo}"#)
- .file("src/foo.tex", "")
- .root_dir(".")
- .build()
- .await;
-
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
- test_bed.open("src/main.tex").await;
-
- let actual_links = test_bed.document_link("src/main.tex").await.unwrap();
-
- test_bed.shutdown().await;
-
- let expected_links = vec![DocumentLink {
- range: Range::new_simple(0, 9, 0, 16),
- target: test_bed.uri("src/foo.tex").into(),
- tooltip: None,
- }];
- assert_eq!(actual_links, expected_links);
-}
-
-#[tokio::test]
-async fn parent_directory() {
- let mut test_bed = TestBedBuilder::new()
- .file("src/main.tex", r#"\addbibresource{../foo.bib}"#)
- .file("foo.bib", "")
- .build()
- .await;
-
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
- test_bed.open("src/main.tex").await;
-
- let actual_links = test_bed.document_link("src/main.tex").await.unwrap();
-
- test_bed.shutdown().await;
-
- let expected_links = vec![DocumentLink {
- range: Range::new_simple(0, 16, 0, 26),
- target: test_bed.uri("foo.bib").into(),
- tooltip: None,
- }];
- assert_eq!(actual_links, expected_links);
-}
-
-#[tokio::test]
-async fn unknown_file() {
- let mut test_bed = TestBedBuilder::new().build().await;
-
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
-
- let actual_links = test_bed.document_link("main.tex").await;
-
- test_bed.shutdown().await;
-
- assert_eq!(actual_links, None);
-}
-
-#[tokio::test]
-async fn edit_file() {
- let mut test_bed = TestBedBuilder::new()
- .file("main.tex", "")
- .file("foo.tex", "")
- .build()
- .await;
-
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
- test_bed.open("main.tex").await;
- test_bed.edit("main.tex", r#"\include{foo}"#).await;
-
- let actual_links = test_bed.document_link("main.tex").await.unwrap();
-
- test_bed.shutdown().await;
-
- let expected_links = vec![DocumentLink {
- range: Range::new_simple(0, 9, 0, 12),
- target: test_bed.uri("foo.tex").into(),
- tooltip: None,
- }];
- assert_eq!(actual_links, expected_links);
-}
-
-#[tokio::test]
-async fn did_change_configuration() {
- let mut test_bed = TestBedBuilder::new()
- .file("src/main.tex", r#"\include{src/foo}"#)
- .file("src/foo.tex", "")
- .root_dir(".")
- .build()
- .await;
-
- test_bed.spawn();
- test_bed.initialize(PUSH_CAPABILITIES.clone()).await;
- test_bed.open("src/main.tex").await;
- test_bed.push_options().await;
-
- let actual_links = test_bed.document_link("src/main.tex").await.unwrap();
-
- test_bed.shutdown().await;
-
- let expected_links = vec![DocumentLink {
- range: Range::new_simple(0, 9, 0, 16),
- target: test_bed.uri("src/foo.tex").into(),
- tooltip: None,
- }];
- assert_eq!(actual_links, expected_links);
-}
diff --git a/support/texlab/tests/integration/main.rs b/support/texlab/tests/integration/main.rs
index 99c0b23a5c..ebccca7396 100644
--- a/support/texlab/tests/integration/main.rs
+++ b/support/texlab/tests/integration/main.rs
@@ -1,12 +1,7 @@
+mod common;
+#[cfg(feature = "completion")]
mod completion;
-mod definition;
mod document_symbol;
-mod folding;
-mod highlight;
mod hover;
mod issues;
-mod link;
-mod prepare_rename;
-mod reference;
-mod rename;
mod workspace_symbol;
diff --git a/support/texlab/tests/integration/prepare_rename.rs b/support/texlab/tests/integration/prepare_rename.rs
deleted file mode 100644
index 0efaa712b2..0000000000
--- a/support/texlab/tests/integration/prepare_rename.rs
+++ /dev/null
@@ -1,151 +0,0 @@
-use texlab::{
- protocol::{Range, RangeExt},
- test::{TestBedBuilder, PULL_CAPABILITIES},
-};
-
-#[tokio::test]
-async fn empty_latex_document() {
- let mut test_bed = TestBedBuilder::new().file("main.tex", "").build().await;
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
- test_bed.open("main.tex").await;
-
- let actual_range = test_bed.prepare_rename("main.tex", 0, 0).await.unwrap();
-
- test_bed.shutdown().await;
-
- assert_eq!(actual_range, None);
-}
-
-#[tokio::test]
-async fn empty_bibtex_document() {
- let mut test_bed = TestBedBuilder::new().file("main.bib", "").build().await;
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
- test_bed.open("main.bib").await;
-
- let actual_range = test_bed.prepare_rename("main.bib", 0, 0).await.unwrap();
-
- test_bed.shutdown().await;
-
- assert_eq!(actual_range, None);
-}
-
-#[tokio::test]
-async fn bibtex_entry() {
- let mut test_bed = TestBedBuilder::new()
- .file("main.bib", r#"@article{foo,}"#)
- .build()
- .await;
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
- test_bed.open("main.bib").await;
-
- let actual_range = test_bed
- .prepare_rename("main.bib", 0, 10)
- .await
- .unwrap()
- .unwrap();
-
- test_bed.shutdown().await;
-
- assert_eq!(actual_range, Range::new_simple(0, 9, 0, 12));
-}
-
-#[tokio::test]
-async fn latex_citation() {
- let mut test_bed = TestBedBuilder::new()
- .file("main.tex", r#"\cite{foo,bar}"#)
- .build()
- .await;
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
- test_bed.open("main.tex").await;
-
- let actual_range = test_bed
- .prepare_rename("main.tex", 0, 11)
- .await
- .unwrap()
- .unwrap();
-
- test_bed.shutdown().await;
-
- assert_eq!(actual_range, Range::new_simple(0, 10, 0, 13));
-}
-
-#[tokio::test]
-async fn latex_command() {
- let mut test_bed = TestBedBuilder::new()
- .file("main.tex", r#"\foo"#)
- .build()
- .await;
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
- test_bed.open("main.tex").await;
-
- let actual_range = test_bed
- .prepare_rename("main.tex", 0, 1)
- .await
- .unwrap()
- .unwrap();
-
- test_bed.shutdown().await;
-
- assert_eq!(actual_range, Range::new_simple(0, 0, 0, 4));
-}
-
-#[tokio::test]
-async fn latex_environment() {
- let mut test_bed = TestBedBuilder::new()
- .file("main.tex", r#"\begin{foo}\end{bar}"#)
- .build()
- .await;
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
- test_bed.open("main.tex").await;
-
- let actual_range = test_bed
- .prepare_rename("main.tex", 0, 7)
- .await
- .unwrap()
- .unwrap();
-
- test_bed.shutdown().await;
-
- assert_eq!(actual_range, Range::new_simple(0, 7, 0, 10));
-}
-
-#[tokio::test]
-async fn latex_label() {
- let mut test_bed = TestBedBuilder::new()
- .file("main.tex", r#"\ref{foo,bar}"#)
- .build()
- .await;
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
- test_bed.open("main.tex").await;
-
- let actual_range = test_bed
- .prepare_rename("main.tex", 0, 9)
- .await
- .unwrap()
- .unwrap();
-
- test_bed.shutdown().await;
-
- assert_eq!(actual_range, Range::new_simple(0, 9, 0, 12));
-}
-
-#[tokio::test]
-async fn unknown_file() {
- let mut test_bed = TestBedBuilder::new().build().await;
-
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
-
- let actual_range = test_bed.prepare_rename("main.tex", 0, 0).await;
-
- test_bed.shutdown().await;
-
- assert_eq!(actual_range, None);
-}
diff --git a/support/texlab/tests/integration/reference.rs b/support/texlab/tests/integration/reference.rs
deleted file mode 100644
index 224f85fbfc..0000000000
--- a/support/texlab/tests/integration/reference.rs
+++ /dev/null
@@ -1,150 +0,0 @@
-use indoc::indoc;
-use texlab::{
- protocol::{Location, Range, RangeExt},
- test::{TestBedBuilder, PULL_CAPABILITIES},
-};
-
-#[tokio::test]
-async fn empty_latex_document() {
- let mut test_bed = TestBedBuilder::new().file("main.tex", "").build().await;
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
- test_bed.open("main.tex").await;
-
- let actual_refs = test_bed.references("main.tex", 0, 0, false).await.unwrap();
-
- test_bed.shutdown().await;
-
- assert!(actual_refs.is_empty());
-}
-
-#[tokio::test]
-async fn empty_bibtex_document() {
- let mut test_bed = TestBedBuilder::new().file("main.bib", "").build().await;
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
- test_bed.open("main.bib").await;
-
- let actual_refs = test_bed.references("main.bib", 0, 0, false).await.unwrap();
-
- test_bed.shutdown().await;
-
- assert!(actual_refs.is_empty());
-}
-
-#[tokio::test]
-async fn bibtex_entry() {
- let mut test_bed = TestBedBuilder::new()
- .file("main.bib", r#"@article{foo,}"#)
- .file(
- "main.tex",
- indoc!(
- r#"
- \addbibresource{main.bib}
- \cite{foo}
- "#
- ),
- )
- .build()
- .await;
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
- test_bed.open("main.bib").await;
- test_bed.open("main.tex").await;
-
- let actual_refs = test_bed.references("main.tex", 1, 8, true).await.unwrap();
-
- test_bed.shutdown().await;
-
- let expected_refs = vec![
- Location {
- uri: test_bed.uri("main.tex").into(),
- range: Range::new_simple(1, 6, 1, 9),
- },
- Location {
- uri: test_bed.uri("main.bib").into(),
- range: Range::new_simple(0, 9, 0, 12),
- },
- ];
-
- assert_eq!(actual_refs, expected_refs);
-}
-
-#[tokio::test]
-async fn bibtex_string() {
- let mut test_bed = TestBedBuilder::new()
- .file(
- "main.bib",
- indoc!(
- r#"
- @string{foo = "foo"}
- @article{bar, author = foo # foo}
- "#
- ),
- )
- .build()
- .await;
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
- test_bed.open("main.bib").await;
-
- let actual_refs = test_bed.references("main.bib", 1, 31, false).await.unwrap();
-
- test_bed.shutdown().await;
-
- let expected_refs = vec![
- Location {
- uri: test_bed.uri("main.bib").into(),
- range: Range::new_simple(1, 23, 1, 26),
- },
- Location {
- uri: test_bed.uri("main.bib").into(),
- range: Range::new_simple(1, 29, 1, 32),
- },
- ];
-
- assert_eq!(actual_refs, expected_refs);
-}
-
-#[tokio::test]
-async fn latex_label() {
- let mut test_bed = TestBedBuilder::new()
- .file(
- "main.tex",
- indoc!(
- r#"
- \label{foo}
- \ref{foo}
- "#
- ),
- )
- .build()
- .await;
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
- test_bed.open("main.tex").await;
-
- let actual_refs = test_bed.references("main.tex", 0, 7, false).await.unwrap();
-
- test_bed.shutdown().await;
-
- let expected_refs = vec![Location {
- uri: test_bed.uri("main.tex").into(),
- range: Range::new_simple(1, 5, 1, 8),
- }];
-
- assert_eq!(actual_refs, expected_refs);
-}
-
-#[tokio::test]
-async fn unknown_file() {
- let mut test_bed = TestBedBuilder::new().build().await;
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
-
- let actual_refs = test_bed.references("main.tex", 0, 0, false).await;
-
- test_bed.shutdown().await;
-
- assert_eq!(actual_refs, None);
-}
diff --git a/support/texlab/tests/integration/rename.rs b/support/texlab/tests/integration/rename.rs
deleted file mode 100644
index d8522f1ec6..0000000000
--- a/support/texlab/tests/integration/rename.rs
+++ /dev/null
@@ -1,236 +0,0 @@
-use indoc::indoc;
-use std::collections::HashMap;
-use texlab::{
- protocol::{Range, RangeExt, TextEdit, WorkspaceEdit},
- test::{TestBedBuilder, PULL_CAPABILITIES},
-};
-
-#[tokio::test]
-async fn empty_latex_document() {
- let mut test_bed = TestBedBuilder::new().file("main.tex", "").build().await;
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
- test_bed.open("main.tex").await;
-
- let actual_edit = test_bed.rename("main.tex", 0, 0, "").await.unwrap();
-
- test_bed.shutdown().await;
-
- assert_eq!(actual_edit, None);
-}
-
-#[tokio::test]
-async fn empty_bibtex_document() {
- let mut test_bed = TestBedBuilder::new().file("main.bib", "").build().await;
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
- test_bed.open("main.bib").await;
-
- let actual_edit = test_bed.rename("main.bib", 0, 0, "").await.unwrap();
-
- test_bed.shutdown().await;
-
- assert_eq!(actual_edit, None);
-}
-
-#[tokio::test]
-async fn bibtex_entry() {
- let mut test_bed = TestBedBuilder::new()
- .file(
- "main.bib",
- indoc!(
- r#"
- @article{foo,}
- @article{bar,}
- "#
- ),
- )
- .file(
- "main.tex",
- indoc!(
- r#"
- \addbibresource{main.bib}
- \cite{foo,bar}
- "#
- ),
- )
- .build()
- .await;
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
- test_bed.open("main.bib").await;
- test_bed.open("main.tex").await;
-
- let actual_edit = test_bed
- .rename("main.bib", 1, 10, "baz")
- .await
- .unwrap()
- .unwrap();
-
- test_bed.shutdown().await;
-
- let mut expected_changes = HashMap::new();
- expected_changes.insert(
- test_bed.uri("main.bib").into(),
- vec![TextEdit::new(Range::new_simple(1, 9, 1, 12), "baz".into())],
- );
- expected_changes.insert(
- test_bed.uri("main.tex").into(),
- vec![TextEdit::new(Range::new_simple(1, 10, 1, 13), "baz".into())],
- );
-
- assert_eq!(actual_edit, WorkspaceEdit::new(expected_changes));
-}
-
-#[tokio::test]
-async fn latex_command() {
- let mut test_bed = TestBedBuilder::new()
- .file(
- "foo.tex",
- indoc!(
- r#"
- \input{bar.tex}
- \foo
- "#
- ),
- )
- .file(
- "bar.tex",
- indoc!(
- r#"
- \foo
- \bar
- "#
- ),
- )
- .build()
- .await;
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
- test_bed.open("foo.tex").await;
- test_bed.open("bar.tex").await;
-
- let actual_edit = test_bed
- .rename("foo.tex", 1, 2, "baz")
- .await
- .unwrap()
- .unwrap();
-
- test_bed.shutdown().await;
-
- let mut expected_changes = HashMap::new();
- expected_changes.insert(
- test_bed.uri("foo.tex").into(),
- vec![TextEdit::new(Range::new_simple(1, 0, 1, 4), "\\baz".into())],
- );
- expected_changes.insert(
- test_bed.uri("bar.tex").into(),
- vec![TextEdit::new(Range::new_simple(0, 0, 0, 4), "\\baz".into())],
- );
-
- assert_eq!(actual_edit, WorkspaceEdit::new(expected_changes));
-}
-
-#[tokio::test]
-async fn latex_environment() {
- let mut test_bed = TestBedBuilder::new()
- .file(
- "main.tex",
- indoc!(
- r#"
- \begin{foo}
- \begin{bar}
- Baz
- \end{bar}
- \end{foo}
- "#
- ),
- )
- .build()
- .await;
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
- test_bed.open("main.tex").await;
-
- let actual_edit = test_bed
- .rename("main.tex", 3, 11, "baz")
- .await
- .unwrap()
- .unwrap();
-
- test_bed.shutdown().await;
-
- let mut expected_changes = HashMap::new();
- expected_changes.insert(
- test_bed.uri("main.tex").into(),
- vec![
- TextEdit::new(Range::new_simple(1, 11, 1, 14), "baz".into()),
- TextEdit::new(Range::new_simple(3, 9, 3, 12), "baz".into()),
- ],
- );
-
- assert_eq!(actual_edit, WorkspaceEdit::new(expected_changes));
-}
-
-#[tokio::test]
-async fn latex_label() {
- let mut test_bed = TestBedBuilder::new()
- .file(
- "foo.tex",
- indoc!(
- r#"
- \input{bar.tex}
- \ref{foo,bar}
- "#
- ),
- )
- .file(
- "bar.tex",
- indoc!(
- r#"
- \label{foo}
- \label{bar}
- "#
- ),
- )
- .build()
- .await;
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
- test_bed.open("foo.tex").await;
- test_bed.open("bar.tex").await;
-
- let actual_edit = test_bed
- .rename("foo.tex", 1, 10, "baz")
- .await
- .unwrap()
- .unwrap();
-
- test_bed.shutdown().await;
-
- let mut expected_changes = HashMap::new();
- expected_changes.insert(
- test_bed.uri("foo.tex").into(),
- vec![TextEdit::new(Range::new_simple(1, 9, 1, 12), "baz".into())],
- );
- expected_changes.insert(
- test_bed.uri("bar.tex").into(),
- vec![TextEdit::new(Range::new_simple(1, 7, 1, 10), "baz".into())],
- );
-
- assert_eq!(actual_edit, WorkspaceEdit::new(expected_changes));
-}
-
-#[tokio::test]
-async fn unknown_file() {
- let mut test_bed = TestBedBuilder::new().build().await;
-
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
-
- let actual_edit = test_bed.rename("main.tex", 0, 0, "").await;
-
- test_bed.shutdown().await;
-
- assert_eq!(actual_edit, None);
-}
diff --git a/support/texlab/tests/integration/snapshots/integration__completion__bibtex__command_complete_entry.snap b/support/texlab/tests/integration/snapshots/integration__completion__bibtex__command_complete_entry.snap
new file mode 100644
index 0000000000..acd3389f49
--- /dev/null
+++ b/support/texlab/tests/integration/snapshots/integration__completion__bibtex__command_complete_entry.snap
@@ -0,0 +1,150 @@
+---
+source: tests/integration/completion.rs
+expression: "complete_and_resolve(&server, uri, 1, 18)?"
+
+---
+{
+ "isIncomplete": true,
+ "items": [
+ {
+ "label": "LaTeX",
+ "kind": 1,
+ "detail": "built-in",
+ "preselect": false,
+ "sortText": "00",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 1,
+ "character": 15
+ },
+ "end": {
+ "line": 1,
+ "character": 18
+ }
+ },
+ "newText": "LaTeX"
+ }
+ },
+ {
+ "label": "LaTeXe",
+ "kind": 1,
+ "detail": "built-in",
+ "preselect": false,
+ "sortText": "01",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 1,
+ "character": 15
+ },
+ "end": {
+ "line": 1,
+ "character": 18
+ }
+ },
+ "newText": "LaTeXe"
+ }
+ },
+ {
+ "label": "latexreleaseversion",
+ "kind": 1,
+ "detail": "built-in",
+ "preselect": false,
+ "sortText": "02",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 1,
+ "character": 15
+ },
+ "end": {
+ "line": 1,
+ "character": 18
+ }
+ },
+ "newText": "latexreleaseversion"
+ }
+ },
+ {
+ "label": "LastDeclaredEncoding",
+ "kind": 1,
+ "detail": "built-in",
+ "preselect": false,
+ "sortText": "03",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 1,
+ "character": 15
+ },
+ "end": {
+ "line": 1,
+ "character": 18
+ }
+ },
+ "newText": "LastDeclaredEncoding"
+ }
+ },
+ {
+ "label": "last",
+ "kind": 1,
+ "detail": "built-in",
+ "preselect": false,
+ "sortText": "04",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 1,
+ "character": 15
+ },
+ "end": {
+ "line": 1,
+ "character": 18
+ }
+ },
+ "newText": "last"
+ }
+ },
+ {
+ "label": "lastbox",
+ "kind": 1,
+ "detail": "built-in",
+ "preselect": false,
+ "sortText": "05",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 1,
+ "character": 15
+ },
+ "end": {
+ "line": 1,
+ "character": 18
+ }
+ },
+ "newText": "lastbox"
+ }
+ },
+ {
+ "label": "lastkern",
+ "kind": 1,
+ "detail": "built-in",
+ "preselect": false,
+ "sortText": "06",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 1,
+ "character": 15
+ },
+ "end": {
+ "line": 1,
+ "character": 18
+ }
+ },
+ "newText": "lastkern"
+ }
+ }
+ ]
+}
diff --git a/support/texlab/tests/integration/snapshots/integration__completion__bibtex__command_incomplete_entry.snap b/support/texlab/tests/integration/snapshots/integration__completion__bibtex__command_incomplete_entry.snap
new file mode 100644
index 0000000000..acd3389f49
--- /dev/null
+++ b/support/texlab/tests/integration/snapshots/integration__completion__bibtex__command_incomplete_entry.snap
@@ -0,0 +1,150 @@
+---
+source: tests/integration/completion.rs
+expression: "complete_and_resolve(&server, uri, 1, 18)?"
+
+---
+{
+ "isIncomplete": true,
+ "items": [
+ {
+ "label": "LaTeX",
+ "kind": 1,
+ "detail": "built-in",
+ "preselect": false,
+ "sortText": "00",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 1,
+ "character": 15
+ },
+ "end": {
+ "line": 1,
+ "character": 18
+ }
+ },
+ "newText": "LaTeX"
+ }
+ },
+ {
+ "label": "LaTeXe",
+ "kind": 1,
+ "detail": "built-in",
+ "preselect": false,
+ "sortText": "01",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 1,
+ "character": 15
+ },
+ "end": {
+ "line": 1,
+ "character": 18
+ }
+ },
+ "newText": "LaTeXe"
+ }
+ },
+ {
+ "label": "latexreleaseversion",
+ "kind": 1,
+ "detail": "built-in",
+ "preselect": false,
+ "sortText": "02",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 1,
+ "character": 15
+ },
+ "end": {
+ "line": 1,
+ "character": 18
+ }
+ },
+ "newText": "latexreleaseversion"
+ }
+ },
+ {
+ "label": "LastDeclaredEncoding",
+ "kind": 1,
+ "detail": "built-in",
+ "preselect": false,
+ "sortText": "03",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 1,
+ "character": 15
+ },
+ "end": {
+ "line": 1,
+ "character": 18
+ }
+ },
+ "newText": "LastDeclaredEncoding"
+ }
+ },
+ {
+ "label": "last",
+ "kind": 1,
+ "detail": "built-in",
+ "preselect": false,
+ "sortText": "04",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 1,
+ "character": 15
+ },
+ "end": {
+ "line": 1,
+ "character": 18
+ }
+ },
+ "newText": "last"
+ }
+ },
+ {
+ "label": "lastbox",
+ "kind": 1,
+ "detail": "built-in",
+ "preselect": false,
+ "sortText": "05",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 1,
+ "character": 15
+ },
+ "end": {
+ "line": 1,
+ "character": 18
+ }
+ },
+ "newText": "lastbox"
+ }
+ },
+ {
+ "label": "lastkern",
+ "kind": 1,
+ "detail": "built-in",
+ "preselect": false,
+ "sortText": "06",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 1,
+ "character": 15
+ },
+ "end": {
+ "line": 1,
+ "character": 18
+ }
+ },
+ "newText": "lastkern"
+ }
+ }
+ ]
+}
diff --git a/support/texlab/tests/integration/snapshots/integration__completion__bibtex__empty_document.snap b/support/texlab/tests/integration/snapshots/integration__completion__bibtex__empty_document.snap
new file mode 100644
index 0000000000..7f8638455c
--- /dev/null
+++ b/support/texlab/tests/integration/snapshots/integration__completion__bibtex__empty_document.snap
@@ -0,0 +1,9 @@
+---
+source: tests/integration/completion.rs
+expression: "complete_and_resolve(&server, uri, 0, 0)?"
+
+---
+{
+ "isIncomplete": false,
+ "items": []
+}
diff --git a/support/texlab/tests/integration/snapshots/integration__completion__bibtex__entry_type_complete.snap b/support/texlab/tests/integration/snapshots/integration__completion__bibtex__entry_type_complete.snap
new file mode 100644
index 0000000000..fdc29dacb4
--- /dev/null
+++ b/support/texlab/tests/integration/snapshots/integration__completion__bibtex__entry_type_complete.snap
@@ -0,0 +1,33 @@
+---
+source: tests/integration/completion.rs
+expression: "complete_and_resolve(&server, uri, 0, 1)?"
+
+---
+{
+ "isIncomplete": false,
+ "items": [
+ {
+ "label": "article",
+ "kind": 1,
+ "documentation": {
+ "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`"
+ },
+ "preselect": false,
+ "sortText": "00",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 0,
+ "character": 1
+ },
+ "end": {
+ "line": 0,
+ "character": 8
+ }
+ },
+ "newText": "article"
+ }
+ }
+ ]
+}
diff --git a/support/texlab/tests/integration/snapshots/integration__completion__bibtex__entry_type_empty_name.snap b/support/texlab/tests/integration/snapshots/integration__completion__bibtex__entry_type_empty_name.snap
new file mode 100644
index 0000000000..ad571951ed
--- /dev/null
+++ b/support/texlab/tests/integration/snapshots/integration__completion__bibtex__entry_type_empty_name.snap
@@ -0,0 +1,159 @@
+---
+source: tests/integration/completion.rs
+expression: "complete_and_resolve(&server, uri, 0, 1)?"
+
+---
+{
+ "isIncomplete": true,
+ "items": [
+ {
+ "label": "preamble",
+ "kind": 1,
+ "preselect": false,
+ "sortText": "00",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 0,
+ "character": 1
+ },
+ "end": {
+ "line": 0,
+ "character": 1
+ }
+ },
+ "newText": "preamble"
+ }
+ },
+ {
+ "label": "string",
+ "kind": 1,
+ "preselect": false,
+ "sortText": "01",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 0,
+ "character": 1
+ },
+ "end": {
+ "line": 0,
+ "character": 1
+ }
+ },
+ "newText": "string"
+ }
+ },
+ {
+ "label": "comment",
+ "kind": 1,
+ "preselect": false,
+ "sortText": "02",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 0,
+ "character": 1
+ },
+ "end": {
+ "line": 0,
+ "character": 1
+ }
+ },
+ "newText": "comment"
+ }
+ },
+ {
+ "label": "article",
+ "kind": 1,
+ "documentation": {
+ "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`"
+ },
+ "preselect": false,
+ "sortText": "03",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 0,
+ "character": 1
+ },
+ "end": {
+ "line": 0,
+ "character": 1
+ }
+ },
+ "newText": "article"
+ }
+ },
+ {
+ "label": "book",
+ "kind": 1,
+ "documentation": {
+ "kind": "markdown",
+ "value": "A single-volume book with one or more authors where the authors share credit for\n the work as a whole. This entry type also covers the function of the `@inbook` type\n of traditional BibTeX.\n\nRequired fields: `author`, `title`, `year/date`"
+ },
+ "preselect": false,
+ "sortText": "04",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 0,
+ "character": 1
+ },
+ "end": {
+ "line": 0,
+ "character": 1
+ }
+ },
+ "newText": "book"
+ }
+ },
+ {
+ "label": "mvbook",
+ "kind": 1,
+ "documentation": {
+ "kind": "markdown",
+ "value": "A multi-volume `@book`. For backwards compatibility, multi-volume books are also\n supported by the entry type `@book`. However, it is advisable to make use of the\n dedicated entry type `@mvbook`.\n\nRequired fields: `author`, `title`, `year/date`"
+ },
+ "preselect": false,
+ "sortText": "05",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 0,
+ "character": 1
+ },
+ "end": {
+ "line": 0,
+ "character": 1
+ }
+ },
+ "newText": "mvbook"
+ }
+ },
+ {
+ "label": "inbook",
+ "kind": 1,
+ "documentation": {
+ "kind": "markdown",
+ "value": "A part of a book which forms a self-contained unit with its own title. Note that the\n profile of this entry type is different from standard BibTeX.\n\nRequired fields: `author`, `title`, `booktitle`, `year/date`"
+ },
+ "preselect": false,
+ "sortText": "06",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 0,
+ "character": 1
+ },
+ "end": {
+ "line": 0,
+ "character": 1
+ }
+ },
+ "newText": "inbook"
+ }
+ }
+ ]
+}
diff --git a/support/texlab/tests/integration/snapshots/integration__completion__bibtex__entry_type_empty_name_before.snap b/support/texlab/tests/integration/snapshots/integration__completion__bibtex__entry_type_empty_name_before.snap
new file mode 100644
index 0000000000..7f8638455c
--- /dev/null
+++ b/support/texlab/tests/integration/snapshots/integration__completion__bibtex__entry_type_empty_name_before.snap
@@ -0,0 +1,9 @@
+---
+source: tests/integration/completion.rs
+expression: "complete_and_resolve(&server, uri, 0, 0)?"
+
+---
+{
+ "isIncomplete": false,
+ "items": []
+}
diff --git a/support/texlab/tests/integration/snapshots/integration__completion__bibtex__entry_type_incomplete.snap b/support/texlab/tests/integration/snapshots/integration__completion__bibtex__entry_type_incomplete.snap
new file mode 100644
index 0000000000..2f3236bfc4
--- /dev/null
+++ b/support/texlab/tests/integration/snapshots/integration__completion__bibtex__entry_type_incomplete.snap
@@ -0,0 +1,79 @@
+---
+source: tests/integration/completion.rs
+expression: "complete_and_resolve(&server, uri, 0, 1)?"
+
+---
+{
+ "isIncomplete": false,
+ "items": [
+ {
+ "label": "article",
+ "kind": 1,
+ "documentation": {
+ "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`"
+ },
+ "preselect": false,
+ "sortText": "00",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 0,
+ "character": 1
+ },
+ "end": {
+ "line": 0,
+ "character": 4
+ }
+ },
+ "newText": "article"
+ }
+ },
+ {
+ "label": "artwork",
+ "kind": 1,
+ "documentation": {
+ "kind": "markdown",
+ "value": "Works of the visual arts such as paintings, sculpture, and installations."
+ },
+ "preselect": false,
+ "sortText": "01",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 0,
+ "character": 1
+ },
+ "end": {
+ "line": 0,
+ "character": 4
+ }
+ },
+ "newText": "artwork"
+ }
+ },
+ {
+ "label": "mastersthesis",
+ "kind": 1,
+ "documentation": {
+ "kind": "markdown",
+ "value": "Similar to `@thesis` except that the `type` field is optional and defaults to the\n localised term ‘Master’s thesis’. You may still use the `type` field to override that."
+ },
+ "preselect": false,
+ "sortText": "02",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 0,
+ "character": 1
+ },
+ "end": {
+ "line": 0,
+ "character": 4
+ }
+ },
+ "newText": "mastersthesis"
+ }
+ }
+ ]
+}
diff --git a/support/texlab/tests/integration/snapshots/integration__completion__bibtex__field_complete_entry.snap b/support/texlab/tests/integration/snapshots/integration__completion__bibtex__field_complete_entry.snap
new file mode 100644
index 0000000000..8c6f330647
--- /dev/null
+++ b/support/texlab/tests/integration/snapshots/integration__completion__bibtex__field_complete_entry.snap
@@ -0,0 +1,171 @@
+---
+source: tests/integration/completion.rs
+expression: "complete_and_resolve(&server, uri, 1, 6)?"
+
+---
+{
+ "isIncomplete": false,
+ "items": [
+ {
+ "label": "title",
+ "kind": 1,
+ "documentation": {
+ "kind": "markdown",
+ "value": "The title of the work."
+ },
+ "preselect": false,
+ "sortText": "00",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 1,
+ "character": 4
+ },
+ "end": {
+ "line": 1,
+ "character": 9
+ }
+ },
+ "newText": "title"
+ }
+ },
+ {
+ "label": "titleaddon",
+ "kind": 1,
+ "documentation": {
+ "kind": "markdown",
+ "value": "An annex to the `title`, to be printed in a different font."
+ },
+ "preselect": false,
+ "sortText": "01",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 1,
+ "character": 4
+ },
+ "end": {
+ "line": 1,
+ "character": 9
+ }
+ },
+ "newText": "titleaddon"
+ }
+ },
+ {
+ "label": "booksubtitle",
+ "kind": 1,
+ "documentation": {
+ "kind": "markdown",
+ "value": "The subtitle related to the `booktitle`. If the subtitle field refers to a work which is part of a larger publication, a possible subtitle of the main work is given in this field. See also `subtitle`."
+ },
+ "preselect": false,
+ "sortText": "02",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 1,
+ "character": 4
+ },
+ "end": {
+ "line": 1,
+ "character": 9
+ }
+ },
+ "newText": "booksubtitle"
+ }
+ },
+ {
+ "label": "booktitle",
+ "kind": 1,
+ "documentation": {
+ "kind": "markdown",
+ "value": "If the `title` field indicates the title of a work which is part of a larger publication, the title of the main work is given in this field. See also `title`."
+ },
+ "preselect": false,
+ "sortText": "03",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 1,
+ "character": 4
+ },
+ "end": {
+ "line": 1,
+ "character": 9
+ }
+ },
+ "newText": "booktitle"
+ }
+ },
+ {
+ "label": "booktitleaddon",
+ "kind": 1,
+ "documentation": {
+ "kind": "markdown",
+ "value": "An annex to the `booktitle`, to be printed in a different font."
+ },
+ "preselect": false,
+ "sortText": "04",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 1,
+ "character": 4
+ },
+ "end": {
+ "line": 1,
+ "character": 9
+ }
+ },
+ "newText": "booktitleaddon"
+ }
+ },
+ {
+ "label": "eventtitle",
+ "kind": 1,
+ "documentation": {
+ "kind": "markdown",
+ "value": "The title of a conference, a symposium, or some other event in `@proceedings` and `@inproceedings` entries. Note that this field holds the plain title of the event. Things like \"Proceedings of the Fifth XYZ Conference\" go into the `titleaddon` or `booktitleaddon` field, respectively. See also `eventdate` and `venue`."
+ },
+ "preselect": false,
+ "sortText": "05",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 1,
+ "character": 4
+ },
+ "end": {
+ "line": 1,
+ "character": 9
+ }
+ },
+ "newText": "eventtitle"
+ }
+ },
+ {
+ "label": "eventtitleaddon",
+ "kind": 1,
+ "documentation": {
+ "kind": "markdown",
+ "value": "An annex to the `eventtitle` field. Can be used for known event acronyms, for example."
+ },
+ "preselect": false,
+ "sortText": "06",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 1,
+ "character": 4
+ },
+ "end": {
+ "line": 1,
+ "character": 9
+ }
+ },
+ "newText": "eventtitleaddon"
+ }
+ }
+ ]
+}
diff --git a/support/texlab/tests/integration/snapshots/integration__completion__bibtex__field_incomplete_entry.snap b/support/texlab/tests/integration/snapshots/integration__completion__bibtex__field_incomplete_entry.snap
new file mode 100644
index 0000000000..72e803cd36
--- /dev/null
+++ b/support/texlab/tests/integration/snapshots/integration__completion__bibtex__field_incomplete_entry.snap
@@ -0,0 +1,171 @@
+---
+source: tests/integration/completion.rs
+expression: "complete_and_resolve(&server, uri, 1, 6)?"
+
+---
+{
+ "isIncomplete": false,
+ "items": [
+ {
+ "label": "title",
+ "kind": 1,
+ "documentation": {
+ "kind": "markdown",
+ "value": "The title of the work."
+ },
+ "preselect": false,
+ "sortText": "00",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 1,
+ "character": 4
+ },
+ "end": {
+ "line": 1,
+ "character": 8
+ }
+ },
+ "newText": "title"
+ }
+ },
+ {
+ "label": "titleaddon",
+ "kind": 1,
+ "documentation": {
+ "kind": "markdown",
+ "value": "An annex to the `title`, to be printed in a different font."
+ },
+ "preselect": false,
+ "sortText": "01",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 1,
+ "character": 4
+ },
+ "end": {
+ "line": 1,
+ "character": 8
+ }
+ },
+ "newText": "titleaddon"
+ }
+ },
+ {
+ "label": "booksubtitle",
+ "kind": 1,
+ "documentation": {
+ "kind": "markdown",
+ "value": "The subtitle related to the `booktitle`. If the subtitle field refers to a work which is part of a larger publication, a possible subtitle of the main work is given in this field. See also `subtitle`."
+ },
+ "preselect": false,
+ "sortText": "02",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 1,
+ "character": 4
+ },
+ "end": {
+ "line": 1,
+ "character": 8
+ }
+ },
+ "newText": "booksubtitle"
+ }
+ },
+ {
+ "label": "booktitle",
+ "kind": 1,
+ "documentation": {
+ "kind": "markdown",
+ "value": "If the `title` field indicates the title of a work which is part of a larger publication, the title of the main work is given in this field. See also `title`."
+ },
+ "preselect": false,
+ "sortText": "03",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 1,
+ "character": 4
+ },
+ "end": {
+ "line": 1,
+ "character": 8
+ }
+ },
+ "newText": "booktitle"
+ }
+ },
+ {
+ "label": "booktitleaddon",
+ "kind": 1,
+ "documentation": {
+ "kind": "markdown",
+ "value": "An annex to the `booktitle`, to be printed in a different font."
+ },
+ "preselect": false,
+ "sortText": "04",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 1,
+ "character": 4
+ },
+ "end": {
+ "line": 1,
+ "character": 8
+ }
+ },
+ "newText": "booktitleaddon"
+ }
+ },
+ {
+ "label": "eventtitle",
+ "kind": 1,
+ "documentation": {
+ "kind": "markdown",
+ "value": "The title of a conference, a symposium, or some other event in `@proceedings` and `@inproceedings` entries. Note that this field holds the plain title of the event. Things like \"Proceedings of the Fifth XYZ Conference\" go into the `titleaddon` or `booktitleaddon` field, respectively. See also `eventdate` and `venue`."
+ },
+ "preselect": false,
+ "sortText": "05",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 1,
+ "character": 4
+ },
+ "end": {
+ "line": 1,
+ "character": 8
+ }
+ },
+ "newText": "eventtitle"
+ }
+ },
+ {
+ "label": "eventtitleaddon",
+ "kind": 1,
+ "documentation": {
+ "kind": "markdown",
+ "value": "An annex to the `eventtitle` field. Can be used for known event acronyms, for example."
+ },
+ "preselect": false,
+ "sortText": "06",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 1,
+ "character": 4
+ },
+ "end": {
+ "line": 1,
+ "character": 8
+ }
+ },
+ "newText": "eventtitleaddon"
+ }
+ }
+ ]
+}
diff --git a/support/texlab/tests/integration/snapshots/integration__completion__bibtex__junk.snap b/support/texlab/tests/integration/snapshots/integration__completion__bibtex__junk.snap
new file mode 100644
index 0000000000..7f8638455c
--- /dev/null
+++ b/support/texlab/tests/integration/snapshots/integration__completion__bibtex__junk.snap
@@ -0,0 +1,9 @@
+---
+source: tests/integration/completion.rs
+expression: "complete_and_resolve(&server, uri, 0, 0)?"
+
+---
+{
+ "isIncomplete": false,
+ "items": []
+}
diff --git a/support/texlab/tests/integration/snapshots/integration__completion__latex__begin_command.snap b/support/texlab/tests/integration/snapshots/integration__completion__latex__begin_command.snap
new file mode 100644
index 0000000000..ab5ea7765b
--- /dev/null
+++ b/support/texlab/tests/integration/snapshots/integration__completion__latex__begin_command.snap
@@ -0,0 +1,162 @@
+---
+source: tests/integration/completion.rs
+expression: "complete_and_resolve(&server, uri, 0, 1)?"
+
+---
+{
+ "isIncomplete": true,
+ "items": [
+ {
+ "label": "begin",
+ "kind": 1,
+ "detail": "built-in",
+ "preselect": false,
+ "sortText": "00",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 0,
+ "character": 1
+ },
+ "end": {
+ "line": 0,
+ "character": 2
+ }
+ },
+ "newText": "begin"
+ }
+ },
+ {
+ "label": "b",
+ "kind": 1,
+ "detail": "built-in",
+ "documentation": {
+ "kind": "markdown",
+ "value": "![b](data:image/png;base64,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|width=48,height=48)"
+ },
+ "preselect": false,
+ "sortText": "01",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 0,
+ "character": 1
+ },
+ "end": {
+ "line": 0,
+ "character": 2
+ }
+ },
+ "newText": "b"
+ }
+ },
+ {
+ "label": "backslash",
+ "kind": 1,
+ "detail": "\\, built-in",
+ "documentation": {
+ "kind": "markdown",
+ "value": "![backslash](data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABwAAAA6CAIAAAAsmq7oAAADD0lEQVR4nO2Y3Uv6UBzGv5stsyhCiUTRytJIDTMkRUEqhmnbVTf+o9sygqjAi4RIVqlJKWWl1FB6oyLt/C68K9tb5+YHncudw4fnczjbcxiBEALcg8RO/IN+ha6srKTT6ZeXF5xQSZJ2d3fPzs5wQjc2Nlqt1vb29m+O2lcoy7IWiyWTyTw+PmKDer3eUCgkiuLp6Sk26NDQEMMwT09PW1tb+ncAfRuFQsHhcEQiEUmSvs+qGT3O6dTUVDgcLhQKoijqC9oDOjAwwLLs29vb5ubm5+enHmrP/Ofn5y6XKxgMNhoNPPoA4HA4YrFYuVw+PDzUEbQ3tL+/n2GYTqcjCEK73dZM/Unh8vJydnbW5/PVajU8+gBgs9ni8XilUsnlclqD/gjt6+tjWZYgCI7jPj4+tFFlLG5ububn52dmZiqVCh59ABgfH19dXa3VatlsVlNQOajBYFhfX6coiuf59/d3DVR5kbu7u1AoNDExUSqV8OgDgMVioWn69vZ2f39ffVAFKEmSyWRycHCQ5/nX11e1VEWXZrMZjUZtNpsoinj0AWB0dHRtbe3+/n5nZwep+2wrQwmCSCQSIyMjgiCorG5Vlwmv1xsIBI6OjkqlEjbo8PBwMplUX92qoARB0DTdre6Hhwc8UADweDyLi4vHx8dqqlsttFvdz8/PmUxGubjUv3wnJyd2uz0cDitWt4arpMvlWlpaKhaL+XxefqUGqMlk6la3IAidTkduqXp9hFC5XJ6cnFxYWKjX63j0AcDpdEajUcXq1gY1Go3d6uY4Tq66NekjhKrVqtvtnpubu7q6wqMPAHa7PR6PV6vVg4ODn9ZohlIUxTCMQnVr1UcIXV9f+3y+6enpi4sLPPoAYLVal5eXZapbD9RgMDAMQ1EUx3G9q1uHPkKo0WgEg0Gn01ksFvHoA8DY2BhN0/V6fW9v7/usTihJkqlUymQy8Tzfo7j06SOEJEmKRCJWqzWXy+HRBwCz2ZxIJCRJ4nkeW1KEUDabNZvNfr8fW1IA8Pv9sVjMaDR+eU6gvx8z/wX0H6vA4+khaPAcAAAAAElFTkSuQmCC|width=48,height=48)"
+ },
+ "preselect": false,
+ "sortText": "02",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 0,
+ "character": 1
+ },
+ "end": {
+ "line": 0,
+ "character": 2
+ }
+ },
+ "newText": "backslash"
+ }
+ },
+ {
+ "label": "badness",
+ "kind": 1,
+ "detail": "built-in",
+ "preselect": false,
+ "sortText": "03",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 0,
+ "character": 1
+ },
+ "end": {
+ "line": 0,
+ "character": 2
+ }
+ },
+ "newText": "badness"
+ }
+ },
+ {
+ "label": "bar",
+ "kind": 1,
+ "detail": " ̄, built-in",
+ "documentation": {
+ "kind": "markdown",
+ "value": "![bar](data:image/png;base64,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|width=48,height=48)"
+ },
+ "preselect": false,
+ "sortText": "04",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 0,
+ "character": 1
+ },
+ "end": {
+ "line": 0,
+ "character": 2
+ }
+ },
+ "newText": "bar"
+ }
+ },
+ {
+ "label": "baselineskip",
+ "kind": 1,
+ "detail": "built-in",
+ "preselect": false,
+ "sortText": "05",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 0,
+ "character": 1
+ },
+ "end": {
+ "line": 0,
+ "character": 2
+ }
+ },
+ "newText": "baselineskip"
+ }
+ },
+ {
+ "label": "baselinestretch",
+ "kind": 1,
+ "detail": "built-in",
+ "preselect": false,
+ "sortText": "06",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 0,
+ "character": 1
+ },
+ "end": {
+ "line": 0,
+ "character": 2
+ }
+ },
+ "newText": "baselinestretch"
+ }
+ }
+ ]
+}
diff --git a/support/texlab/tests/integration/snapshots/integration__completion__latex__citation.snap b/support/texlab/tests/integration/snapshots/integration__completion__latex__citation.snap
new file mode 100644
index 0000000000..1ccf0206c8
--- /dev/null
+++ b/support/texlab/tests/integration/snapshots/integration__completion__latex__citation.snap
@@ -0,0 +1,54 @@
+---
+source: tests/integration/completion.rs
+expression: "complete_and_resolve(&server, tex_uri, 3, 6)?"
+
+---
+{
+ "isIncomplete": false,
+ "items": [
+ {
+ "label": "foo:2019",
+ "kind": 1,
+ "documentation": {
+ "kind": "markdown",
+ "value": "Bar, F. (2019). *Baz Qux*."
+ },
+ "preselect": false,
+ "sortText": "00 foo:2019 @articlefoo:2019 author Foo Bar title Baz Qux year 2019",
+ "filterText": "foo:2019 @articlefoo:2019 author Foo Bar title Baz Qux year 2019",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 3,
+ "character": 6
+ },
+ "end": {
+ "line": 3,
+ "character": 6
+ }
+ },
+ "newText": "foo:2019"
+ }
+ },
+ {
+ "label": "bar:2005",
+ "kind": 1,
+ "preselect": false,
+ "sortText": "01 bar:2005 @articlebar:2005",
+ "filterText": "bar:2005 @articlebar:2005",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 3,
+ "character": 6
+ },
+ "end": {
+ "line": 3,
+ "character": 6
+ }
+ },
+ "newText": "bar:2005"
+ }
+ }
+ ]
+}
diff --git a/support/texlab/tests/integration/snapshots/integration__completion__latex__citation_after.snap b/support/texlab/tests/integration/snapshots/integration__completion__latex__citation_after.snap
new file mode 100644
index 0000000000..156b28dc02
--- /dev/null
+++ b/support/texlab/tests/integration/snapshots/integration__completion__latex__citation_after.snap
@@ -0,0 +1,9 @@
+---
+source: tests/integration/completion.rs
+expression: "complete_and_resolve(&server, tex_uri, 3, 7)?"
+
+---
+{
+ "isIncomplete": false,
+ "items": []
+}
diff --git a/support/texlab/tests/integration/snapshots/integration__completion__latex__citation_open_brace.snap b/support/texlab/tests/integration/snapshots/integration__completion__latex__citation_open_brace.snap
new file mode 100644
index 0000000000..ba2a1c7ce3
--- /dev/null
+++ b/support/texlab/tests/integration/snapshots/integration__completion__latex__citation_open_brace.snap
@@ -0,0 +1,34 @@
+---
+source: tests/integration/completion.rs
+expression: "complete_and_resolve(&server, tex_uri, 3, 9)?"
+
+---
+{
+ "isIncomplete": false,
+ "items": [
+ {
+ "label": "FooBar",
+ "kind": 1,
+ "documentation": {
+ "kind": "markdown",
+ "value": "Bar, F. (2019). *Baz Qux*."
+ },
+ "preselect": false,
+ "sortText": "00 FooBar @articleFooBar author Foo Bar title Baz Qux year 2019",
+ "filterText": "FooBar @articleFooBar author Foo Bar title Baz Qux year 2019",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 3,
+ "character": 6
+ },
+ "end": {
+ "line": 3,
+ "character": 9
+ }
+ },
+ "newText": "FooBar"
+ }
+ }
+ ]
+}
diff --git a/support/texlab/tests/integration/snapshots/integration__completion__latex__class_command.snap b/support/texlab/tests/integration/snapshots/integration__completion__latex__class_command.snap
new file mode 100644
index 0000000000..ff214e764a
--- /dev/null
+++ b/support/texlab/tests/integration/snapshots/integration__completion__latex__class_command.snap
@@ -0,0 +1,150 @@
+---
+source: tests/integration/completion.rs
+expression: "complete_and_resolve(&server, uri, 2, 5)?"
+
+---
+{
+ "isIncomplete": false,
+ "items": [
+ {
+ "label": "chaptername",
+ "kind": 1,
+ "detail": "book.cls",
+ "preselect": false,
+ "sortText": "00",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 2,
+ "character": 1
+ },
+ "end": {
+ "line": 2,
+ "character": 5
+ }
+ },
+ "newText": "chaptername"
+ }
+ },
+ {
+ "label": "chaptermark",
+ "kind": 1,
+ "detail": "book.cls",
+ "preselect": false,
+ "sortText": "01",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 2,
+ "character": 1
+ },
+ "end": {
+ "line": 2,
+ "character": 5
+ }
+ },
+ "newText": "chaptermark"
+ }
+ },
+ {
+ "label": "chapter",
+ "kind": 1,
+ "detail": "book.cls",
+ "preselect": false,
+ "sortText": "02",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 2,
+ "character": 1
+ },
+ "end": {
+ "line": 2,
+ "character": 5
+ }
+ },
+ "newText": "chapter"
+ }
+ },
+ {
+ "label": "thechapter",
+ "kind": 1,
+ "detail": "book.cls",
+ "preselect": false,
+ "sortText": "03",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 2,
+ "character": 1
+ },
+ "end": {
+ "line": 2,
+ "character": 5
+ }
+ },
+ "newText": "thechapter"
+ }
+ },
+ {
+ "label": "scshape",
+ "kind": 1,
+ "detail": "built-in",
+ "preselect": false,
+ "sortText": "04",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 2,
+ "character": 1
+ },
+ "end": {
+ "line": 2,
+ "character": 5
+ }
+ },
+ "newText": "scshape"
+ }
+ },
+ {
+ "label": "fontchardp",
+ "kind": 1,
+ "detail": "amsmath.sty",
+ "preselect": false,
+ "sortText": "05",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 2,
+ "character": 1
+ },
+ "end": {
+ "line": 2,
+ "character": 5
+ }
+ },
+ "newText": "fontchardp"
+ }
+ },
+ {
+ "label": "XeTeXcharglyph",
+ "kind": 1,
+ "detail": "built-in",
+ "preselect": false,
+ "sortText": "06",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 2,
+ "character": 1
+ },
+ "end": {
+ "line": 2,
+ "character": 5
+ }
+ },
+ "newText": "XeTeXcharglyph"
+ }
+ }
+ ]
+}
diff --git a/support/texlab/tests/integration/snapshots/integration__completion__latex__class_environment.snap b/support/texlab/tests/integration/snapshots/integration__completion__latex__class_environment.snap
new file mode 100644
index 0000000000..c893498077
--- /dev/null
+++ b/support/texlab/tests/integration/snapshots/integration__completion__latex__class_environment.snap
@@ -0,0 +1,30 @@
+---
+source: tests/integration/completion.rs
+expression: "complete_and_resolve(&server, uri, 4, 13)?"
+
+---
+{
+ "isIncomplete": false,
+ "items": [
+ {
+ "label": "theindex",
+ "kind": 1,
+ "detail": "book.cls",
+ "preselect": false,
+ "sortText": "00",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 4,
+ "character": 7
+ },
+ "end": {
+ "line": 4,
+ "character": 13
+ }
+ },
+ "newText": "theindex"
+ }
+ }
+ ]
+}
diff --git a/support/texlab/tests/integration/snapshots/integration__completion__latex__class_import.snap b/support/texlab/tests/integration/snapshots/integration__completion__latex__class_import.snap
new file mode 100644
index 0000000000..baf1218114
--- /dev/null
+++ b/support/texlab/tests/integration/snapshots/integration__completion__latex__class_import.snap
@@ -0,0 +1,171 @@
+---
+source: tests/integration/completion.rs
+expression: "complete_and_resolve(&server, uri, 0, 19)?"
+
+---
+{
+ "isIncomplete": false,
+ "items": [
+ {
+ "label": "book",
+ "kind": 1,
+ "documentation": {
+ "kind": "plaintext",
+ "value": "LaTeX is a widely-used macro package for TeX, providing many basic document formating commands extended by a wide range of packages. It is a development of Leslie Lamport's LaTeX 2.09, and superseded the older system in June 1994. The basic distribution is catalogued separately, at latex-base; apart from a large set of contributed packages and third-party documentation (elsewhere on the archive), the distribution includes: - a bunch of required packages, which LaTeX authors are \"entitled to assume\" will be present on any system running LaTeX; and - a minimal set of documentation detailing differences from the 'old' version of LaTeX in the areas of user commands, font selection and control, class and package writing, font encodings, configuration options and modification of LaTeX. For downloading details, see the linked catalogue entries above."
+ },
+ "preselect": false,
+ "sortText": "00",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 0,
+ "character": 15
+ },
+ "end": {
+ "line": 0,
+ "character": 19
+ }
+ },
+ "newText": "book"
+ }
+ },
+ {
+ "label": "bookcover",
+ "kind": 1,
+ "documentation": {
+ "kind": "plaintext",
+ "value": "This class helps typesetting book covers and dust jackets."
+ },
+ "preselect": false,
+ "sortText": "01",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 0,
+ "character": 15
+ },
+ "end": {
+ "line": 0,
+ "character": 19
+ }
+ },
+ "newText": "bookcover"
+ }
+ },
+ {
+ "label": "bookest",
+ "kind": 1,
+ "documentation": {
+ "kind": "plaintext",
+ "value": "The class extends the standard book class, in the areas of colour scheme management, document layout, headings and footers, front page layout, and other minor items."
+ },
+ "preselect": false,
+ "sortText": "02",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 0,
+ "character": 15
+ },
+ "end": {
+ "line": 0,
+ "character": 19
+ }
+ },
+ "newText": "bookest"
+ }
+ },
+ {
+ "label": "bbook",
+ "kind": 1,
+ "documentation": {
+ "kind": "plaintext",
+ "value": "The bundle provides class files for writing Bangla and Assamese with LaTeX, and Metafont sources for fonts."
+ },
+ "preselect": false,
+ "sortText": "03",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 0,
+ "character": 15
+ },
+ "end": {
+ "line": 0,
+ "character": 19
+ }
+ },
+ "newText": "bbook"
+ }
+ },
+ {
+ "label": "basque-book",
+ "kind": 1,
+ "documentation": {
+ "kind": "plaintext",
+ "value": "The class is derived from the LaTeX book class. The extensions solve grammatical and numeration issues that occur when book-type documents are written in Basque. The class is useful for writing books, PhD and Master Theses, etc., in Basque."
+ },
+ "preselect": false,
+ "sortText": "04",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 0,
+ "character": 15
+ },
+ "end": {
+ "line": 0,
+ "character": 19
+ }
+ },
+ "newText": "basque-book"
+ }
+ },
+ {
+ "label": "caesar_book",
+ "kind": 1,
+ "documentation": {
+ "kind": "plaintext",
+ "value": "The package allows typesetting of texts with notes, figures, citations, captions and tables in the margin. This is common (for example) in science text books."
+ },
+ "preselect": false,
+ "sortText": "05",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 0,
+ "character": 15
+ },
+ "end": {
+ "line": 0,
+ "character": 19
+ }
+ },
+ "newText": "caesar_book"
+ }
+ },
+ {
+ "label": "tufte-book",
+ "kind": 1,
+ "documentation": {
+ "kind": "plaintext",
+ "value": "Provided are two classes inspired, respectively, by handouts and books created by Edward Tufte."
+ },
+ "preselect": false,
+ "sortText": "06",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 0,
+ "character": 15
+ },
+ "end": {
+ "line": 0,
+ "character": 19
+ }
+ },
+ "newText": "tufte-book"
+ }
+ }
+ ]
+}
diff --git a/support/texlab/tests/integration/snapshots/integration__completion__latex__color_model_define_color.snap b/support/texlab/tests/integration/snapshots/integration__completion__latex__color_model_define_color.snap
new file mode 100644
index 0000000000..5385f5c646
--- /dev/null
+++ b/support/texlab/tests/integration/snapshots/integration__completion__latex__color_model_define_color.snap
@@ -0,0 +1,105 @@
+---
+source: tests/integration/completion.rs
+expression: "complete_and_resolve(&server, uri, 1, 18)?"
+
+---
+{
+ "isIncomplete": false,
+ "items": [
+ {
+ "label": "gray",
+ "kind": 1,
+ "preselect": false,
+ "sortText": "00",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 1,
+ "character": 18
+ },
+ "end": {
+ "line": 1,
+ "character": 18
+ }
+ },
+ "newText": "gray"
+ }
+ },
+ {
+ "label": "rgb",
+ "kind": 1,
+ "preselect": false,
+ "sortText": "01",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 1,
+ "character": 18
+ },
+ "end": {
+ "line": 1,
+ "character": 18
+ }
+ },
+ "newText": "rgb"
+ }
+ },
+ {
+ "label": "RGB",
+ "kind": 1,
+ "preselect": false,
+ "sortText": "02",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 1,
+ "character": 18
+ },
+ "end": {
+ "line": 1,
+ "character": 18
+ }
+ },
+ "newText": "RGB"
+ }
+ },
+ {
+ "label": "HTML",
+ "kind": 1,
+ "preselect": false,
+ "sortText": "03",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 1,
+ "character": 18
+ },
+ "end": {
+ "line": 1,
+ "character": 18
+ }
+ },
+ "newText": "HTML"
+ }
+ },
+ {
+ "label": "cmyk",
+ "kind": 1,
+ "preselect": false,
+ "sortText": "04",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 1,
+ "character": 18
+ },
+ "end": {
+ "line": 1,
+ "character": 18
+ }
+ },
+ "newText": "cmyk"
+ }
+ }
+ ]
+}
diff --git a/support/texlab/tests/integration/snapshots/integration__completion__latex__color_model_define_color_set.snap b/support/texlab/tests/integration/snapshots/integration__completion__latex__color_model_define_color_set.snap
new file mode 100644
index 0000000000..7e6f4a1de6
--- /dev/null
+++ b/support/texlab/tests/integration/snapshots/integration__completion__latex__color_model_define_color_set.snap
@@ -0,0 +1,67 @@
+---
+source: tests/integration/completion.rs
+expression: "complete_and_resolve(&server, uri, 2, 17)?"
+
+---
+{
+ "isIncomplete": false,
+ "items": [
+ {
+ "label": "RGB",
+ "kind": 1,
+ "preselect": false,
+ "sortText": "00",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 2,
+ "character": 16
+ },
+ "end": {
+ "line": 2,
+ "character": 17
+ }
+ },
+ "newText": "RGB"
+ }
+ },
+ {
+ "label": "rgb",
+ "kind": 1,
+ "preselect": false,
+ "sortText": "01",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 2,
+ "character": 16
+ },
+ "end": {
+ "line": 2,
+ "character": 17
+ }
+ },
+ "newText": "rgb"
+ }
+ },
+ {
+ "label": "gray",
+ "kind": 1,
+ "preselect": false,
+ "sortText": "02",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 2,
+ "character": 16
+ },
+ "end": {
+ "line": 2,
+ "character": 17
+ }
+ },
+ "newText": "gray"
+ }
+ }
+ ]
+}
diff --git a/support/texlab/tests/integration/snapshots/integration__completion__latex__color_name.snap b/support/texlab/tests/integration/snapshots/integration__completion__latex__color_name.snap
new file mode 100644
index 0000000000..b34b32f5ac
--- /dev/null
+++ b/support/texlab/tests/integration/snapshots/integration__completion__latex__color_name.snap
@@ -0,0 +1,143 @@
+---
+source: tests/integration/completion.rs
+expression: "complete_and_resolve(&server, uri, 0, 9)?"
+
+---
+{
+ "isIncomplete": false,
+ "items": [
+ {
+ "label": "red",
+ "kind": 1,
+ "preselect": false,
+ "sortText": "00",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 0,
+ "character": 7
+ },
+ "end": {
+ "line": 0,
+ "character": 9
+ }
+ },
+ "newText": "red"
+ }
+ },
+ {
+ "label": "RedOrange",
+ "kind": 1,
+ "preselect": false,
+ "sortText": "01",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 0,
+ "character": 7
+ },
+ "end": {
+ "line": 0,
+ "character": 9
+ }
+ },
+ "newText": "RedOrange"
+ }
+ },
+ {
+ "label": "Red",
+ "kind": 1,
+ "preselect": false,
+ "sortText": "02",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 0,
+ "character": 7
+ },
+ "end": {
+ "line": 0,
+ "character": 9
+ }
+ },
+ "newText": "Red"
+ }
+ },
+ {
+ "label": "RedViolet",
+ "kind": 1,
+ "preselect": false,
+ "sortText": "03",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 0,
+ "character": 7
+ },
+ "end": {
+ "line": 0,
+ "character": 9
+ }
+ },
+ "newText": "RedViolet"
+ }
+ },
+ {
+ "label": "OrangeRed",
+ "kind": 1,
+ "preselect": false,
+ "sortText": "04",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 0,
+ "character": 7
+ },
+ "end": {
+ "line": 0,
+ "character": 9
+ }
+ },
+ "newText": "OrangeRed"
+ }
+ },
+ {
+ "label": "VioletRed",
+ "kind": 1,
+ "preselect": false,
+ "sortText": "05",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 0,
+ "character": 7
+ },
+ "end": {
+ "line": 0,
+ "character": 9
+ }
+ },
+ "newText": "VioletRed"
+ }
+ },
+ {
+ "label": "BrickRed",
+ "kind": 1,
+ "preselect": false,
+ "sortText": "06",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 0,
+ "character": 7
+ },
+ "end": {
+ "line": 0,
+ "character": 9
+ }
+ },
+ "newText": "BrickRed"
+ }
+ }
+ ]
+}
diff --git a/support/texlab/tests/integration/snapshots/integration__completion__latex__empty_document.snap b/support/texlab/tests/integration/snapshots/integration__completion__latex__empty_document.snap
new file mode 100644
index 0000000000..7f8638455c
--- /dev/null
+++ b/support/texlab/tests/integration/snapshots/integration__completion__latex__empty_document.snap
@@ -0,0 +1,9 @@
+---
+source: tests/integration/completion.rs
+expression: "complete_and_resolve(&server, uri, 0, 0)?"
+
+---
+{
+ "isIncomplete": false,
+ "items": []
+}
diff --git a/support/texlab/tests/integration/snapshots/integration__completion__latex__kernel_command.snap b/support/texlab/tests/integration/snapshots/integration__completion__latex__kernel_command.snap
new file mode 100644
index 0000000000..49f65f06ca
--- /dev/null
+++ b/support/texlab/tests/integration/snapshots/integration__completion__latex__kernel_command.snap
@@ -0,0 +1,30 @@
+---
+source: tests/integration/completion.rs
+expression: "complete_and_resolve(&server, uri, 0, 1)?"
+
+---
+{
+ "isIncomplete": false,
+ "items": [
+ {
+ "label": "documentclass",
+ "kind": 1,
+ "detail": "built-in",
+ "preselect": false,
+ "sortText": "00",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 0,
+ "character": 1
+ },
+ "end": {
+ "line": 0,
+ "character": 14
+ }
+ },
+ "newText": "documentclass"
+ }
+ }
+ ]
+}
diff --git a/support/texlab/tests/integration/snapshots/integration__completion__latex__kernel_command_environment.snap b/support/texlab/tests/integration/snapshots/integration__completion__latex__kernel_command_environment.snap
new file mode 100644
index 0000000000..8d01ea7b8d
--- /dev/null
+++ b/support/texlab/tests/integration/snapshots/integration__completion__latex__kernel_command_environment.snap
@@ -0,0 +1,30 @@
+---
+source: tests/integration/completion.rs
+expression: "complete_and_resolve(&server, uri, 6, 10)?"
+
+---
+{
+ "isIncomplete": false,
+ "items": [
+ {
+ "label": "document",
+ "kind": 1,
+ "detail": "built-in",
+ "preselect": false,
+ "sortText": "00",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 6,
+ "character": 7
+ },
+ "end": {
+ "line": 6,
+ "character": 10
+ }
+ },
+ "newText": "document"
+ }
+ }
+ ]
+}
diff --git a/support/texlab/tests/integration/snapshots/integration__completion__latex__kernel_command_glyph.snap b/support/texlab/tests/integration/snapshots/integration__completion__latex__kernel_command_glyph.snap
new file mode 100644
index 0000000000..293b190176
--- /dev/null
+++ b/support/texlab/tests/integration/snapshots/integration__completion__latex__kernel_command_glyph.snap
@@ -0,0 +1,34 @@
+---
+source: tests/integration/completion.rs
+expression: "complete_and_resolve(&server, uri, 7, 7)?"
+
+---
+{
+ "isIncomplete": false,
+ "items": [
+ {
+ "label": "varepsilon",
+ "kind": 1,
+ "detail": "ε, built-in",
+ "documentation": {
+ "kind": "markdown",
+ "value": "![varepsilon](data:image/png;base64,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|width=48,height=48)"
+ },
+ "preselect": false,
+ "sortText": "00",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 7,
+ "character": 1
+ },
+ "end": {
+ "line": 7,
+ "character": 7
+ }
+ },
+ "newText": "varepsilon"
+ }
+ }
+ ]
+}
diff --git a/support/texlab/tests/integration/snapshots/integration__completion__latex__label.snap b/support/texlab/tests/integration/snapshots/integration__completion__latex__label.snap
new file mode 100644
index 0000000000..efcaa02ce1
--- /dev/null
+++ b/support/texlab/tests/integration/snapshots/integration__completion__latex__label.snap
@@ -0,0 +1,137 @@
+---
+source: tests/integration/completion.rs
+expression: "complete_and_resolve(&server, uri, 4, 5)?"
+
+---
+{
+ "isIncomplete": false,
+ "items": [
+ {
+ "label": "sec:bar",
+ "kind": 1,
+ "detail": "Section 2 (Bar)",
+ "preselect": false,
+ "sortText": "00 sec:bar Section 2 (Bar)",
+ "filterText": "sec:bar Section 2 (Bar)",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 4,
+ "character": 5
+ },
+ "end": {
+ "line": 4,
+ "character": 5
+ }
+ },
+ "newText": "sec:bar"
+ }
+ },
+ {
+ "label": "sec:foo",
+ "kind": 1,
+ "detail": "Section 1 (Foo)",
+ "preselect": false,
+ "sortText": "01 sec:foo Section 1 (Foo)",
+ "filterText": "sec:foo Section 1 (Foo)",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 4,
+ "character": 5
+ },
+ "end": {
+ "line": 4,
+ "character": 5
+ }
+ },
+ "newText": "sec:foo"
+ }
+ },
+ {
+ "label": "eq:foo",
+ "kind": 1,
+ "detail": "Equation (1)",
+ "preselect": false,
+ "sortText": "02 eq:foo Equation (1)",
+ "filterText": "eq:foo Equation (1)",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 4,
+ "character": 5
+ },
+ "end": {
+ "line": 4,
+ "character": 5
+ }
+ },
+ "newText": "eq:foo"
+ }
+ },
+ {
+ "label": "eq:bar",
+ "kind": 1,
+ "detail": "Equation (2)",
+ "preselect": false,
+ "sortText": "03 eq:bar Equation (2)",
+ "filterText": "eq:bar Equation (2)",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 4,
+ "character": 5
+ },
+ "end": {
+ "line": 4,
+ "character": 5
+ }
+ },
+ "newText": "eq:bar"
+ }
+ },
+ {
+ "label": "fig:baz",
+ "kind": 1,
+ "detail": "Figure 1",
+ "documentation": "Baz",
+ "preselect": false,
+ "sortText": "04 fig:baz Figure 1: Baz",
+ "filterText": "fig:baz Figure 1: Baz",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 4,
+ "character": 5
+ },
+ "end": {
+ "line": 4,
+ "character": 5
+ }
+ },
+ "newText": "fig:baz"
+ }
+ },
+ {
+ "label": "thm:foo",
+ "kind": 1,
+ "detail": "Lemma 1",
+ "preselect": false,
+ "sortText": "05 thm:foo Lemma 1",
+ "filterText": "thm:foo Lemma 1",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 4,
+ "character": 5
+ },
+ "end": {
+ "line": 4,
+ "character": 5
+ }
+ },
+ "newText": "thm:foo"
+ }
+ }
+ ]
+}
diff --git a/support/texlab/tests/integration/snapshots/integration__completion__latex__package_command.snap b/support/texlab/tests/integration/snapshots/integration__completion__latex__package_command.snap
new file mode 100644
index 0000000000..6a260fe387
--- /dev/null
+++ b/support/texlab/tests/integration/snapshots/integration__completion__latex__package_command.snap
@@ -0,0 +1,34 @@
+---
+source: tests/integration/completion.rs
+expression: "complete_and_resolve(&server, uri, 3, 7)?"
+
+---
+{
+ "isIncomplete": false,
+ "items": [
+ {
+ "label": "varDelta",
+ "kind": 1,
+ "detail": "amsmath.sty",
+ "documentation": {
+ "kind": "markdown",
+ "value": "![varDelta](data:image/png;base64,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|width=48,height=48)"
+ },
+ "preselect": false,
+ "sortText": "00",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 3,
+ "character": 1
+ },
+ "end": {
+ "line": 3,
+ "character": 7
+ }
+ },
+ "newText": "varDelta"
+ }
+ }
+ ]
+}
diff --git a/support/texlab/tests/integration/snapshots/integration__completion__latex__package_environment.snap b/support/texlab/tests/integration/snapshots/integration__completion__latex__package_environment.snap
new file mode 100644
index 0000000000..7dbe95a81b
--- /dev/null
+++ b/support/texlab/tests/integration/snapshots/integration__completion__latex__package_environment.snap
@@ -0,0 +1,150 @@
+---
+source: tests/integration/completion.rs
+expression: "complete_and_resolve(&server, uri, 5, 5)?"
+
+---
+{
+ "isIncomplete": false,
+ "items": [
+ {
+ "label": "alignedat",
+ "kind": 1,
+ "detail": "amsmath.sty",
+ "preselect": false,
+ "sortText": "00",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 5,
+ "character": 5
+ },
+ "end": {
+ "line": 5,
+ "character": 9
+ }
+ },
+ "newText": "alignedat"
+ }
+ },
+ {
+ "label": "aligned",
+ "kind": 1,
+ "detail": "amsmath.sty",
+ "preselect": false,
+ "sortText": "01",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 5,
+ "character": 5
+ },
+ "end": {
+ "line": 5,
+ "character": 9
+ }
+ },
+ "newText": "aligned"
+ }
+ },
+ {
+ "label": "alignat",
+ "kind": 1,
+ "detail": "amsmath.sty",
+ "preselect": false,
+ "sortText": "02",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 5,
+ "character": 5
+ },
+ "end": {
+ "line": 5,
+ "character": 9
+ }
+ },
+ "newText": "alignat"
+ }
+ },
+ {
+ "label": "align",
+ "kind": 1,
+ "detail": "amsmath.sty",
+ "preselect": false,
+ "sortText": "03",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 5,
+ "character": 5
+ },
+ "end": {
+ "line": 5,
+ "character": 9
+ }
+ },
+ "newText": "align"
+ }
+ },
+ {
+ "label": "align*",
+ "kind": 1,
+ "detail": "amsmath.sty",
+ "preselect": false,
+ "sortText": "04",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 5,
+ "character": 5
+ },
+ "end": {
+ "line": 5,
+ "character": 9
+ }
+ },
+ "newText": "align*"
+ }
+ },
+ {
+ "label": "alignat*",
+ "kind": 1,
+ "detail": "amsmath.sty",
+ "preselect": false,
+ "sortText": "05",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 5,
+ "character": 5
+ },
+ "end": {
+ "line": 5,
+ "character": 9
+ }
+ },
+ "newText": "alignat*"
+ }
+ },
+ {
+ "label": "flalign",
+ "kind": 1,
+ "detail": "amsmath.sty",
+ "preselect": false,
+ "sortText": "06",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 5,
+ "character": 5
+ },
+ "end": {
+ "line": 5,
+ "character": 9
+ }
+ },
+ "newText": "flalign"
+ }
+ }
+ ]
+}
diff --git a/support/texlab/tests/integration/snapshots/integration__completion__latex__package_import.snap b/support/texlab/tests/integration/snapshots/integration__completion__latex__package_import.snap
new file mode 100644
index 0000000000..992ea91692
--- /dev/null
+++ b/support/texlab/tests/integration/snapshots/integration__completion__latex__package_import.snap
@@ -0,0 +1,102 @@
+---
+source: tests/integration/completion.rs
+expression: "complete_and_resolve(&server, uri, 1, 15)?"
+
+---
+{
+ "isIncomplete": false,
+ "items": [
+ {
+ "label": "amsmath",
+ "kind": 1,
+ "documentation": {
+ "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."
+ },
+ "preselect": false,
+ "sortText": "00",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 1,
+ "character": 12
+ },
+ "end": {
+ "line": 1,
+ "character": 19
+ }
+ },
+ "newText": "amsmath"
+ }
+ },
+ {
+ "label": "lwarp-amsmath",
+ "kind": 1,
+ "documentation": {
+ "kind": "plaintext",
+ "value": "This package converts LaTeX to HTML by using LaTeX to process the user's document and generate HTML tags. External utility programs are only used for the final conversion of text and images. Math may be represented by SVG files or MathJax. Hundreds of LaTeX packages are supported, and their load order is automatically verified. Documents may be produced by LaTeX, LuaLaTeX, XeLaTeX, and by several CJK engines, classes, and packages. A texlua script automates compilation, index, glossary, and batch image processing, and also supports latexmk. Configuration is semi-automatic at the first manual compile. Support files are self-generated. Print and HTML versions of each document may coexist. Assistance is provided for HTML import into EPUB conversion software and word processors. Requirements include the commonly-available Poppler utilities, and Perl. Detailed installation instructions are included for each of the major operating systems and TeX distributions. A quick-start tutorial is provided."
+ },
+ "preselect": false,
+ "sortText": "01",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 1,
+ "character": 12
+ },
+ "end": {
+ "line": 1,
+ "character": 19
+ }
+ },
+ "newText": "lwarp-amsmath"
+ }
+ },
+ {
+ "label": "simurgh-amsmath",
+ "kind": 1,
+ "documentation": {
+ "kind": "plaintext",
+ "value": "The package provides an automatic and unified interface for Parsi typesetting in LaTeX, using the LuaTeX engine. The project to produce this system is dedicated to Ferdowsi The Great."
+ },
+ "preselect": false,
+ "sortText": "02",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 1,
+ "character": 12
+ },
+ "end": {
+ "line": 1,
+ "character": 19
+ }
+ },
+ "newText": "simurgh-amsmath"
+ }
+ },
+ {
+ "label": "onlyamsmath",
+ "kind": 1,
+ "documentation": {
+ "kind": "plaintext",
+ "value": "This package inhibits the usage of plain TeX and (on demand) of standard LaTeX mathematics environments. This is useful for class writers who want to encourage their users to use the environments provided by the amsmath package."
+ },
+ "preselect": false,
+ "sortText": "03",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 1,
+ "character": 12
+ },
+ "end": {
+ "line": 1,
+ "character": 19
+ }
+ },
+ "newText": "onlyamsmath"
+ }
+ }
+ ]
+}
diff --git a/support/texlab/tests/integration/snapshots/integration__completion__latex__pgf_library.snap b/support/texlab/tests/integration/snapshots/integration__completion__latex__pgf_library.snap
new file mode 100644
index 0000000000..da34563c8d
--- /dev/null
+++ b/support/texlab/tests/integration/snapshots/integration__completion__latex__pgf_library.snap
@@ -0,0 +1,143 @@
+---
+source: tests/integration/completion.rs
+expression: "complete_and_resolve(&server, uri, 1, 15)?"
+
+---
+{
+ "isIncomplete": false,
+ "items": [
+ {
+ "label": "arrows",
+ "kind": 1,
+ "preselect": false,
+ "sortText": "00",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 1,
+ "character": 15
+ },
+ "end": {
+ "line": 1,
+ "character": 15
+ }
+ },
+ "newText": "arrows"
+ }
+ },
+ {
+ "label": "arrows.meta",
+ "kind": 1,
+ "preselect": false,
+ "sortText": "01",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 1,
+ "character": 15
+ },
+ "end": {
+ "line": 1,
+ "character": 15
+ }
+ },
+ "newText": "arrows.meta"
+ }
+ },
+ {
+ "label": "arrows.spaced",
+ "kind": 1,
+ "preselect": false,
+ "sortText": "02",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 1,
+ "character": 15
+ },
+ "end": {
+ "line": 1,
+ "character": 15
+ }
+ },
+ "newText": "arrows.spaced"
+ }
+ },
+ {
+ "label": "curvilinear",
+ "kind": 1,
+ "preselect": false,
+ "sortText": "03",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 1,
+ "character": 15
+ },
+ "end": {
+ "line": 1,
+ "character": 15
+ }
+ },
+ "newText": "curvilinear"
+ }
+ },
+ {
+ "label": "datavisualization.barcharts",
+ "kind": 1,
+ "preselect": false,
+ "sortText": "04",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 1,
+ "character": 15
+ },
+ "end": {
+ "line": 1,
+ "character": 15
+ }
+ },
+ "newText": "datavisualization.barcharts"
+ }
+ },
+ {
+ "label": "datavisualization.formats.functions",
+ "kind": 1,
+ "preselect": false,
+ "sortText": "05",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 1,
+ "character": 15
+ },
+ "end": {
+ "line": 1,
+ "character": 15
+ }
+ },
+ "newText": "datavisualization.formats.functions"
+ }
+ },
+ {
+ "label": "datavisualization.polar",
+ "kind": 1,
+ "preselect": false,
+ "sortText": "06",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 1,
+ "character": 15
+ },
+ "end": {
+ "line": 1,
+ "character": 15
+ }
+ },
+ "newText": "datavisualization.polar"
+ }
+ }
+ ]
+}
diff --git a/support/texlab/tests/integration/snapshots/integration__completion__latex__preselect_environment.snap b/support/texlab/tests/integration/snapshots/integration__completion__latex__preselect_environment.snap
new file mode 100644
index 0000000000..791f045476
--- /dev/null
+++ b/support/texlab/tests/integration/snapshots/integration__completion__latex__preselect_environment.snap
@@ -0,0 +1,150 @@
+---
+source: tests/integration/completion.rs
+expression: "complete_and_resolve(&server, uri, 1, 5)?"
+
+---
+{
+ "isIncomplete": false,
+ "items": [
+ {
+ "label": "document",
+ "kind": 1,
+ "detail": "built-in",
+ "preselect": true,
+ "sortText": "00",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 1,
+ "character": 5
+ },
+ "end": {
+ "line": 1,
+ "character": 5
+ }
+ },
+ "newText": "document"
+ }
+ },
+ {
+ "label": "abstract",
+ "kind": 1,
+ "detail": "built-in",
+ "preselect": false,
+ "sortText": "01",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 1,
+ "character": 5
+ },
+ "end": {
+ "line": 1,
+ "character": 5
+ }
+ },
+ "newText": "abstract"
+ }
+ },
+ {
+ "label": "array",
+ "kind": 1,
+ "detail": "built-in",
+ "preselect": false,
+ "sortText": "02",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 1,
+ "character": 5
+ },
+ "end": {
+ "line": 1,
+ "character": 5
+ }
+ },
+ "newText": "array"
+ }
+ },
+ {
+ "label": "center",
+ "kind": 1,
+ "detail": "built-in",
+ "preselect": false,
+ "sortText": "03",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 1,
+ "character": 5
+ },
+ "end": {
+ "line": 1,
+ "character": 5
+ }
+ },
+ "newText": "center"
+ }
+ },
+ {
+ "label": "csname",
+ "kind": 1,
+ "detail": "built-in",
+ "preselect": false,
+ "sortText": "04",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 1,
+ "character": 5
+ },
+ "end": {
+ "line": 1,
+ "character": 5
+ }
+ },
+ "newText": "csname"
+ }
+ },
+ {
+ "label": "description",
+ "kind": 1,
+ "detail": "built-in",
+ "preselect": false,
+ "sortText": "05",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 1,
+ "character": 5
+ },
+ "end": {
+ "line": 1,
+ "character": 5
+ }
+ },
+ "newText": "description"
+ }
+ },
+ {
+ "label": "displaymath",
+ "kind": 1,
+ "detail": "built-in",
+ "preselect": false,
+ "sortText": "06",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 1,
+ "character": 5
+ },
+ "end": {
+ "line": 1,
+ "character": 5
+ }
+ },
+ "newText": "displaymath"
+ }
+ }
+ ]
+}
diff --git a/support/texlab/tests/integration/snapshots/integration__completion__latex__theorem_environment.snap b/support/texlab/tests/integration/snapshots/integration__completion__latex__theorem_environment.snap
new file mode 100644
index 0000000000..cc0b336e9f
--- /dev/null
+++ b/support/texlab/tests/integration/snapshots/integration__completion__latex__theorem_environment.snap
@@ -0,0 +1,130 @@
+---
+source: tests/integration/completion.rs
+expression: "complete_and_resolve(&server, uri, 3, 7)?"
+
+---
+{
+ "isIncomplete": false,
+ "items": [
+ {
+ "label": "figure",
+ "kind": 1,
+ "detail": "built-in",
+ "preselect": false,
+ "sortText": "00",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 3,
+ "character": 7
+ },
+ "end": {
+ "line": 3,
+ "character": 8
+ }
+ },
+ "newText": "figure"
+ }
+ },
+ {
+ "label": "filecontents",
+ "kind": 1,
+ "detail": "built-in",
+ "preselect": false,
+ "sortText": "01",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 3,
+ "character": 7
+ },
+ "end": {
+ "line": 3,
+ "character": 8
+ }
+ },
+ "newText": "filecontents"
+ }
+ },
+ {
+ "label": "flushleft",
+ "kind": 1,
+ "detail": "built-in",
+ "preselect": false,
+ "sortText": "02",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 3,
+ "character": 7
+ },
+ "end": {
+ "line": 3,
+ "character": 8
+ }
+ },
+ "newText": "flushleft"
+ }
+ },
+ {
+ "label": "flushright",
+ "kind": 1,
+ "detail": "built-in",
+ "preselect": false,
+ "sortText": "03",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 3,
+ "character": 7
+ },
+ "end": {
+ "line": 3,
+ "character": 8
+ }
+ },
+ "newText": "flushright"
+ }
+ },
+ {
+ "label": "foo",
+ "kind": 1,
+ "detail": "user-defined",
+ "preselect": false,
+ "sortText": "04",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 3,
+ "character": 7
+ },
+ "end": {
+ "line": 3,
+ "character": 8
+ }
+ },
+ "newText": "foo"
+ }
+ },
+ {
+ "label": "proof",
+ "kind": 1,
+ "detail": "amsthm.sty",
+ "preselect": false,
+ "sortText": "05",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 3,
+ "character": 7
+ },
+ "end": {
+ "line": 3,
+ "character": 8
+ }
+ },
+ "newText": "proof"
+ }
+ }
+ ]
+}
diff --git a/support/texlab/tests/integration/snapshots/integration__completion__latex__user_command.snap b/support/texlab/tests/integration/snapshots/integration__completion__latex__user_command.snap
new file mode 100644
index 0000000000..f2f2eef4a5
--- /dev/null
+++ b/support/texlab/tests/integration/snapshots/integration__completion__latex__user_command.snap
@@ -0,0 +1,30 @@
+---
+source: tests/integration/completion.rs
+expression: "complete_and_resolve(&server, uri, 1, 3)?"
+
+---
+{
+ "isIncomplete": false,
+ "items": [
+ {
+ "label": "foobar",
+ "kind": 1,
+ "detail": "user-defined",
+ "preselect": false,
+ "sortText": "00",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 1,
+ "character": 1
+ },
+ "end": {
+ "line": 1,
+ "character": 6
+ }
+ },
+ "newText": "foobar"
+ }
+ }
+ ]
+}
diff --git a/support/texlab/tests/integration/snapshots/integration__completion__latex__user_environment.snap b/support/texlab/tests/integration/snapshots/integration__completion__latex__user_environment.snap
new file mode 100644
index 0000000000..0f85b3fa56
--- /dev/null
+++ b/support/texlab/tests/integration/snapshots/integration__completion__latex__user_environment.snap
@@ -0,0 +1,50 @@
+---
+source: tests/integration/completion.rs
+expression: "complete_and_resolve(&server, uri, 4, 7)?"
+
+---
+{
+ "isIncomplete": false,
+ "items": [
+ {
+ "label": "foo",
+ "kind": 1,
+ "detail": "user-defined",
+ "preselect": false,
+ "sortText": "00",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 4,
+ "character": 7
+ },
+ "end": {
+ "line": 4,
+ "character": 9
+ }
+ },
+ "newText": "foo"
+ }
+ },
+ {
+ "label": "filecontents",
+ "kind": 1,
+ "detail": "built-in",
+ "preselect": false,
+ "sortText": "01",
+ "textEdit": {
+ "range": {
+ "start": {
+ "line": 4,
+ "character": 7
+ },
+ "end": {
+ "line": 4,
+ "character": 9
+ }
+ },
+ "newText": "filecontents"
+ }
+ }
+ ]
+}
diff --git a/support/texlab/tests/integration/snapshots/integration__document_symbol__enumerate_flat.snap b/support/texlab/tests/integration/snapshots/integration__document_symbol__enumerate_flat.snap
new file mode 100644
index 0000000000..1f042106a2
--- /dev/null
+++ b/support/texlab/tests/integration/snapshots/integration__document_symbol__enumerate_flat.snap
@@ -0,0 +1,97 @@
+---
+source: tests/integration/document_symbol.rs
+expression: server.find_document_symbols(uri)?
+
+---
+[
+ {
+ "name": "Enumerate",
+ "kind": 10,
+ "deprecated": false,
+ "location": {
+ "uri": "http://www.example.com/main.tex",
+ "range": {
+ "start": {
+ "line": 4,
+ "character": 0
+ },
+ "end": {
+ "line": 9,
+ "character": 15
+ }
+ }
+ }
+ },
+ {
+ "name": "1",
+ "kind": 22,
+ "deprecated": false,
+ "location": {
+ "uri": "http://www.example.com/main.tex",
+ "range": {
+ "start": {
+ "line": 5,
+ "character": 4
+ },
+ "end": {
+ "line": 5,
+ "character": 27
+ }
+ }
+ }
+ },
+ {
+ "name": "Item",
+ "kind": 22,
+ "deprecated": false,
+ "location": {
+ "uri": "http://www.example.com/main.tex",
+ "range": {
+ "start": {
+ "line": 6,
+ "character": 4
+ },
+ "end": {
+ "line": 6,
+ "character": 27
+ }
+ }
+ }
+ },
+ {
+ "name": "Baz",
+ "kind": 22,
+ "deprecated": false,
+ "location": {
+ "uri": "http://www.example.com/main.tex",
+ "range": {
+ "start": {
+ "line": 7,
+ "character": 4
+ },
+ "end": {
+ "line": 7,
+ "character": 18
+ }
+ }
+ }
+ },
+ {
+ "name": "2",
+ "kind": 22,
+ "deprecated": false,
+ "location": {
+ "uri": "http://www.example.com/main.tex",
+ "range": {
+ "start": {
+ "line": 8,
+ "character": 4
+ },
+ "end": {
+ "line": 8,
+ "character": 32
+ }
+ }
+ }
+ }
+]
diff --git a/support/texlab/tests/integration/snapshots/integration__document_symbol__enumerate_nested.snap b/support/texlab/tests/integration/snapshots/integration__document_symbol__enumerate_nested.snap
new file mode 100644
index 0000000000..2584b7ef50
--- /dev/null
+++ b/support/texlab/tests/integration/snapshots/integration__document_symbol__enumerate_nested.snap
@@ -0,0 +1,141 @@
+---
+source: tests/integration/document_symbol.rs
+expression: server.find_document_symbols(uri)?
+
+---
+[
+ {
+ "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/integration/snapshots/integration__document_symbol__equation_flat.snap b/support/texlab/tests/integration/snapshots/integration__document_symbol__equation_flat.snap
new file mode 100644
index 0000000000..1f4b3bfe4d
--- /dev/null
+++ b/support/texlab/tests/integration/snapshots/integration__document_symbol__equation_flat.snap
@@ -0,0 +1,61 @@
+---
+source: tests/integration/document_symbol.rs
+expression: server.find_document_symbols(uri)?
+
+---
+[
+ {
+ "name": "Equation (1)",
+ "kind": 14,
+ "deprecated": false,
+ "location": {
+ "uri": "http://www.example.com/main.tex",
+ "range": {
+ "start": {
+ "line": 4,
+ "character": 0
+ },
+ "end": {
+ "line": 6,
+ "character": 14
+ }
+ }
+ }
+ },
+ {
+ "name": "Equation",
+ "kind": 14,
+ "deprecated": false,
+ "location": {
+ "uri": "http://www.example.com/main.tex",
+ "range": {
+ "start": {
+ "line": 8,
+ "character": 0
+ },
+ "end": {
+ "line": 10,
+ "character": 14
+ }
+ }
+ }
+ },
+ {
+ "name": "Equation",
+ "kind": 14,
+ "deprecated": false,
+ "location": {
+ "uri": "http://www.example.com/main.tex",
+ "range": {
+ "start": {
+ "line": 12,
+ "character": 0
+ },
+ "end": {
+ "line": 14,
+ "character": 14
+ }
+ }
+ }
+ }
+]
diff --git a/support/texlab/tests/integration/snapshots/integration__document_symbol__equation_nested.snap b/support/texlab/tests/integration/snapshots/integration__document_symbol__equation_nested.snap
new file mode 100644
index 0000000000..9ffb191b89
--- /dev/null
+++ b/support/texlab/tests/integration/snapshots/integration__document_symbol__equation_nested.snap
@@ -0,0 +1,87 @@
+---
+source: tests/integration/document_symbol.rs
+expression: server.find_document_symbols(uri)?
+
+---
+[
+ {
+ "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/integration/snapshots/integration__document_symbol__float_flat.snap b/support/texlab/tests/integration/snapshots/integration__document_symbol__float_flat.snap
new file mode 100644
index 0000000000..c7465ddcc0
--- /dev/null
+++ b/support/texlab/tests/integration/snapshots/integration__document_symbol__float_flat.snap
@@ -0,0 +1,61 @@
+---
+source: tests/integration/document_symbol.rs
+expression: server.find_document_symbols(uri)?
+
+---
+[
+ {
+ "name": "Figure 1: Foo",
+ "kind": 6,
+ "deprecated": false,
+ "location": {
+ "uri": "http://www.example.com/main.tex",
+ "range": {
+ "start": {
+ "line": 4,
+ "character": 0
+ },
+ "end": {
+ "line": 7,
+ "character": 12
+ }
+ }
+ }
+ },
+ {
+ "name": "Figure: Bar",
+ "kind": 6,
+ "deprecated": false,
+ "location": {
+ "uri": "http://www.example.com/main.tex",
+ "range": {
+ "start": {
+ "line": 9,
+ "character": 0
+ },
+ "end": {
+ "line": 12,
+ "character": 12
+ }
+ }
+ }
+ },
+ {
+ "name": "Figure: Baz",
+ "kind": 6,
+ "deprecated": false,
+ "location": {
+ "uri": "http://www.example.com/main.tex",
+ "range": {
+ "start": {
+ "line": 14,
+ "character": 0
+ },
+ "end": {
+ "line": 17,
+ "character": 12
+ }
+ }
+ }
+ }
+]
diff --git a/support/texlab/tests/integration/snapshots/integration__document_symbol__float_nested.snap b/support/texlab/tests/integration/snapshots/integration__document_symbol__float_nested.snap
new file mode 100644
index 0000000000..886ffc560c
--- /dev/null
+++ b/support/texlab/tests/integration/snapshots/integration__document_symbol__float_nested.snap
@@ -0,0 +1,87 @@
+---
+source: tests/integration/document_symbol.rs
+expression: server.find_document_symbols(uri)?
+
+---
+[
+ {
+ "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/integration/snapshots/integration__document_symbol__section_flat.snap b/support/texlab/tests/integration/snapshots/integration__document_symbol__section_flat.snap
new file mode 100644
index 0000000000..2f7ad319a7
--- /dev/null
+++ b/support/texlab/tests/integration/snapshots/integration__document_symbol__section_flat.snap
@@ -0,0 +1,61 @@
+---
+source: tests/integration/document_symbol.rs
+expression: server.find_document_symbols(uri)?
+
+---
+[
+ {
+ "name": "Foo",
+ "kind": 2,
+ "deprecated": false,
+ "location": {
+ "uri": "http://www.example.com/main.tex",
+ "range": {
+ "start": {
+ "line": 4,
+ "character": 0
+ },
+ "end": {
+ "line": 4,
+ "character": 13
+ }
+ }
+ }
+ },
+ {
+ "name": "2 Bar",
+ "kind": 2,
+ "deprecated": false,
+ "location": {
+ "uri": "http://www.example.com/main.tex",
+ "range": {
+ "start": {
+ "line": 6,
+ "character": 0
+ },
+ "end": {
+ "line": 8,
+ "character": 31
+ }
+ }
+ }
+ },
+ {
+ "name": "Baz",
+ "kind": 2,
+ "deprecated": false,
+ "location": {
+ "uri": "http://www.example.com/main.tex",
+ "range": {
+ "start": {
+ "line": 8,
+ "character": 0
+ },
+ "end": {
+ "line": 8,
+ "character": 31
+ }
+ }
+ }
+ }
+]
diff --git a/support/texlab/tests/integration/snapshots/integration__document_symbol__section_nested.snap b/support/texlab/tests/integration/snapshots/integration__document_symbol__section_nested.snap
new file mode 100644
index 0000000000..d5b5eb4b0c
--- /dev/null
+++ b/support/texlab/tests/integration/snapshots/integration__document_symbol__section_nested.snap
@@ -0,0 +1,88 @@
+---
+source: tests/integration/document_symbol.rs
+expression: server.find_document_symbols(uri)?
+
+---
+[
+ {
+ "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/integration/snapshots/integration__document_symbol__theorem_flat.snap b/support/texlab/tests/integration/snapshots/integration__document_symbol__theorem_flat.snap
new file mode 100644
index 0000000000..8ce3824bea
--- /dev/null
+++ b/support/texlab/tests/integration/snapshots/integration__document_symbol__theorem_flat.snap
@@ -0,0 +1,79 @@
+---
+source: tests/integration/document_symbol.rs
+expression: server.find_document_symbols(uri)?
+
+---
+[
+ {
+ "name": "Lemma 1 (Foo)",
+ "kind": 13,
+ "deprecated": false,
+ "location": {
+ "uri": "http://www.example.com/main.tex",
+ "range": {
+ "start": {
+ "line": 6,
+ "character": 0
+ },
+ "end": {
+ "line": 8,
+ "character": 11
+ }
+ }
+ }
+ },
+ {
+ "name": "Lemma 2",
+ "kind": 13,
+ "deprecated": false,
+ "location": {
+ "uri": "http://www.example.com/main.tex",
+ "range": {
+ "start": {
+ "line": 10,
+ "character": 0
+ },
+ "end": {
+ "line": 12,
+ "character": 11
+ }
+ }
+ }
+ },
+ {
+ "name": "Lemma",
+ "kind": 13,
+ "deprecated": false,
+ "location": {
+ "uri": "http://www.example.com/main.tex",
+ "range": {
+ "start": {
+ "line": 14,
+ "character": 0
+ },
+ "end": {
+ "line": 16,
+ "character": 11
+ }
+ }
+ }
+ },
+ {
+ "name": "Lemma (Qux)",
+ "kind": 13,
+ "deprecated": false,
+ "location": {
+ "uri": "http://www.example.com/main.tex",
+ "range": {
+ "start": {
+ "line": 18,
+ "character": 0
+ },
+ "end": {
+ "line": 20,
+ "character": 11
+ }
+ }
+ }
+ }
+]
diff --git a/support/texlab/tests/integration/snapshots/integration__document_symbol__theorem_nested.snap b/support/texlab/tests/integration/snapshots/integration__document_symbol__theorem_nested.snap
new file mode 100644
index 0000000000..052f62fec7
--- /dev/null
+++ b/support/texlab/tests/integration/snapshots/integration__document_symbol__theorem_nested.snap
@@ -0,0 +1,114 @@
+---
+source: tests/integration/document_symbol.rs
+expression: server.find_document_symbols(uri)?
+
+---
+[
+ {
+ "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/integration/snapshots/integration__hover__empty_bibtex_document.snap b/support/texlab/tests/integration/snapshots/integration__hover__empty_bibtex_document.snap
new file mode 100644
index 0000000000..e064602aa1
--- /dev/null
+++ b/support/texlab/tests/integration/snapshots/integration__hover__empty_bibtex_document.snap
@@ -0,0 +1,6 @@
+---
+source: tests/integration/hover.rs
+expression: "server.hover(uri, 0, 0)?"
+
+---
+null
diff --git a/support/texlab/tests/integration/snapshots/integration__hover__empty_labtex_document.snap b/support/texlab/tests/integration/snapshots/integration__hover__empty_labtex_document.snap
new file mode 100644
index 0000000000..e064602aa1
--- /dev/null
+++ b/support/texlab/tests/integration/snapshots/integration__hover__empty_labtex_document.snap
@@ -0,0 +1,6 @@
+---
+source: tests/integration/hover.rs
+expression: "server.hover(uri, 0, 0)?"
+
+---
+null
diff --git a/support/texlab/tests/integration/snapshots/integration__hover__label_theorem_child_file.snap b/support/texlab/tests/integration/snapshots/integration__hover__label_theorem_child_file.snap
new file mode 100644
index 0000000000..758c89cfac
--- /dev/null
+++ b/support/texlab/tests/integration/snapshots/integration__hover__label_theorem_child_file.snap
@@ -0,0 +1,21 @@
+---
+source: tests/integration/hover.rs
+expression: "server.hover(uri, 3, 8)?"
+
+---
+{
+ "contents": {
+ "kind": "plaintext",
+ "value": "Lemma"
+ },
+ "range": {
+ "start": {
+ "line": 3,
+ "character": 5
+ },
+ "end": {
+ "line": 3,
+ "character": 12
+ }
+ }
+}
diff --git a/support/texlab/tests/integration/snapshots/integration__hover__label_theorem_child_file_mumber.snap b/support/texlab/tests/integration/snapshots/integration__hover__label_theorem_child_file_mumber.snap
new file mode 100644
index 0000000000..43737f72a1
--- /dev/null
+++ b/support/texlab/tests/integration/snapshots/integration__hover__label_theorem_child_file_mumber.snap
@@ -0,0 +1,21 @@
+---
+source: tests/integration/hover.rs
+expression: "server.hover(uri, 3, 8)?"
+
+---
+{
+ "contents": {
+ "kind": "plaintext",
+ "value": "Lemma 1 (Foo)"
+ },
+ "range": {
+ "start": {
+ "line": 3,
+ "character": 5
+ },
+ "end": {
+ "line": 3,
+ "character": 12
+ }
+ }
+}
diff --git a/support/texlab/tests/integration/snapshots/integration__workspace_symbol__filter_bibtex.snap b/support/texlab/tests/integration/snapshots/integration__workspace_symbol__filter_bibtex.snap
new file mode 100644
index 0000000000..bac8d65144
--- /dev/null
+++ b/support/texlab/tests/integration/snapshots/integration__workspace_symbol__filter_bibtex.snap
@@ -0,0 +1,43 @@
+---
+source: tests/integration/workspace_symbol.rs
+expression: "run(\"bibtex\")?"
+
+---
+[
+ {
+ "name": "foo",
+ "kind": 24,
+ "deprecated": false,
+ "location": {
+ "uri": "http://www.example.com/main.bib",
+ "range": {
+ "start": {
+ "line": 0,
+ "character": 0
+ },
+ "end": {
+ "line": 0,
+ "character": 14
+ }
+ }
+ }
+ },
+ {
+ "name": "bar",
+ "kind": 15,
+ "deprecated": false,
+ "location": {
+ "uri": "http://www.example.com/main.bib",
+ "range": {
+ "start": {
+ "line": 2,
+ "character": 0
+ },
+ "end": {
+ "line": 2,
+ "character": 20
+ }
+ }
+ }
+ }
+]
diff --git a/support/texlab/tests/integration/snapshots/integration__workspace_symbol__filter_type_figure.snap b/support/texlab/tests/integration/snapshots/integration__workspace_symbol__filter_type_figure.snap
new file mode 100644
index 0000000000..8842d02f7d
--- /dev/null
+++ b/support/texlab/tests/integration/snapshots/integration__workspace_symbol__filter_type_figure.snap
@@ -0,0 +1,25 @@
+---
+source: tests/integration/workspace_symbol.rs
+expression: "run(\"figure\")?"
+
+---
+[
+ {
+ "name": "Figure 1: Bar",
+ "kind": 6,
+ "deprecated": false,
+ "location": {
+ "uri": "http://www.example.com/main.tex",
+ "range": {
+ "start": {
+ "line": 15,
+ "character": 0
+ },
+ "end": {
+ "line": 19,
+ "character": 12
+ }
+ }
+ }
+ }
+]
diff --git a/support/texlab/tests/integration/snapshots/integration__workspace_symbol__filter_type_item.snap b/support/texlab/tests/integration/snapshots/integration__workspace_symbol__filter_type_item.snap
new file mode 100644
index 0000000000..3434eaff9c
--- /dev/null
+++ b/support/texlab/tests/integration/snapshots/integration__workspace_symbol__filter_type_item.snap
@@ -0,0 +1,61 @@
+---
+source: tests/integration/workspace_symbol.rs
+expression: "run(\"item\")?"
+
+---
+[
+ {
+ "name": "1",
+ "kind": 22,
+ "deprecated": false,
+ "location": {
+ "uri": "http://www.example.com/main.tex",
+ "range": {
+ "start": {
+ "line": 24,
+ "character": 4
+ },
+ "end": {
+ "line": 24,
+ "character": 28
+ }
+ }
+ }
+ },
+ {
+ "name": "2",
+ "kind": 22,
+ "deprecated": false,
+ "location": {
+ "uri": "http://www.example.com/main.tex",
+ "range": {
+ "start": {
+ "line": 25,
+ "character": 4
+ },
+ "end": {
+ "line": 25,
+ "character": 28
+ }
+ }
+ }
+ },
+ {
+ "name": "3",
+ "kind": 22,
+ "deprecated": false,
+ "location": {
+ "uri": "http://www.example.com/main.tex",
+ "range": {
+ "start": {
+ "line": 26,
+ "character": 4
+ },
+ "end": {
+ "line": 26,
+ "character": 28
+ }
+ }
+ }
+ }
+]
diff --git a/support/texlab/tests/integration/snapshots/integration__workspace_symbol__filter_type_math.snap b/support/texlab/tests/integration/snapshots/integration__workspace_symbol__filter_type_math.snap
new file mode 100644
index 0000000000..d2b7550efe
--- /dev/null
+++ b/support/texlab/tests/integration/snapshots/integration__workspace_symbol__filter_type_math.snap
@@ -0,0 +1,43 @@
+---
+source: tests/integration/workspace_symbol.rs
+expression: "run(\"math\")?"
+
+---
+[
+ {
+ "name": "Equation (1)",
+ "kind": 14,
+ "deprecated": false,
+ "location": {
+ "uri": "http://www.example.com/main.tex",
+ "range": {
+ "start": {
+ "line": 9,
+ "character": 0
+ },
+ "end": {
+ "line": 11,
+ "character": 14
+ }
+ }
+ }
+ },
+ {
+ "name": "Lemma 1 (Qux)",
+ "kind": 13,
+ "deprecated": false,
+ "location": {
+ "uri": "http://www.example.com/main.tex",
+ "range": {
+ "start": {
+ "line": 33,
+ "character": 0
+ },
+ "end": {
+ "line": 35,
+ "character": 11
+ }
+ }
+ }
+ }
+]
diff --git a/support/texlab/tests/integration/snapshots/integration__workspace_symbol__filter_type_section.snap b/support/texlab/tests/integration/snapshots/integration__workspace_symbol__filter_type_section.snap
new file mode 100644
index 0000000000..e221d55dcb
--- /dev/null
+++ b/support/texlab/tests/integration/snapshots/integration__workspace_symbol__filter_type_section.snap
@@ -0,0 +1,79 @@
+---
+source: tests/integration/workspace_symbol.rs
+expression: "run(\"section\")?"
+
+---
+[
+ {
+ "name": "1 Foo",
+ "kind": 2,
+ "deprecated": false,
+ "location": {
+ "uri": "http://www.example.com/main.tex",
+ "range": {
+ "start": {
+ "line": 7,
+ "character": 0
+ },
+ "end": {
+ "line": 11,
+ "character": 14
+ }
+ }
+ }
+ },
+ {
+ "name": "2 Bar",
+ "kind": 2,
+ "deprecated": false,
+ "location": {
+ "uri": "http://www.example.com/main.tex",
+ "range": {
+ "start": {
+ "line": 13,
+ "character": 0
+ },
+ "end": {
+ "line": 19,
+ "character": 12
+ }
+ }
+ }
+ },
+ {
+ "name": "3 Baz",
+ "kind": 2,
+ "deprecated": false,
+ "location": {
+ "uri": "http://www.example.com/main.tex",
+ "range": {
+ "start": {
+ "line": 21,
+ "character": 0
+ },
+ "end": {
+ "line": 27,
+ "character": 15
+ }
+ }
+ }
+ },
+ {
+ "name": "4 Qux",
+ "kind": 2,
+ "deprecated": false,
+ "location": {
+ "uri": "http://www.example.com/main.tex",
+ "range": {
+ "start": {
+ "line": 29,
+ "character": 0
+ },
+ "end": {
+ "line": 35,
+ "character": 11
+ }
+ }
+ }
+ }
+]
diff --git a/support/texlab/tests/integration/workspace_symbol.rs b/support/texlab/tests/integration/workspace_symbol.rs
index 5eb8e1ace7..54a0a8808d 100644
--- a/support/texlab/tests/integration/workspace_symbol.rs
+++ b/support/texlab/tests/integration/workspace_symbol.rs
@@ -1,228 +1,119 @@
-use indoc::indoc;
-use texlab::{
- protocol::{Location, Range, RangeExt, SymbolInformation, WorkspaceSymbolParams},
- test::{TestBed, TestBedBuilder, TestLspClient, PULL_CAPABILITIES},
-};
-
-pub fn verify_symbol_info(
- symbol: &SymbolInformation,
- test_bed: &TestBed,
- relative_path: &str,
- name: &str,
- start_line: u64,
- start_character: u64,
- end_line: u64,
- end_character: u64,
-) {
- assert_eq!(symbol.name, name);
- let range = Range::new_simple(start_line, start_character, end_line, end_character);
- assert_eq!(
- symbol.location,
- Location::new(test_bed.uri(relative_path).into(), range)
- );
-}
+use anyhow::Result;
+use insta::assert_json_snapshot;
+use lsp_types::{ClientCapabilities, SymbolInformation, Url};
+
+use crate::common::ServerTester;
+
+fn run(query: &str) -> Result<Vec<SymbolInformation>> {
+ let server = ServerTester::launch_new_instance()?;
+ server.initialize(ClientCapabilities::default(), None)?;
+ let uri = Url::parse("http://www.example.com/main.tex")?;
+ server.open_memory(
+ uri.clone(),
+ r#"
+ \documentclass{article}
+ \usepackage{caption}
+ \usepackage{amsmath}
+ \usepackage{amsthm}
+
+ \begin{document}
+
+ \section{Foo}\label{sec:foo}
+
+ \begin{equation}\label{eq:foo}
+ Foo
+ \end{equation}
+
+ \section{Bar}\label{sec:bar}
+
+ \begin{figure}
+ Bar
+ \caption{Bar}
+ \label{fig:bar}
+ \end{figure}
+
+ \section{Baz}\label{sec:baz}
+
+ \begin{enumerate}
+ \item\label{itm:foo} Foo
+ \item\label{itm:bar} Bar
+ \item\label{itm:baz} Baz
+ \end{enumerate}
+
+ \section{Qux}\label{sec:qux}
+
+ \newtheorem{lemma}{Lemma}
+
+ \begin{lemma}[Qux]\label{thm:qux}
+ Qux
+ \end{lemma}
+
+ \end{document}
+ "#,
+ "latex",
+ )?;
+ server.open_memory(
+ Url::parse("http://www.example.com/main.aux")?,
+ r#"
+ \relax
+ \@writefile{lof}{\contentsline {figure}{\numberline {1}{\ignorespaces Bar\relax }}{1}\protected@file@percent }
+ \providecommand*\caption@xref[2]{\@setref\relax\@undefined{#1}}
+ \newlabel{fig:bar}{{1}{1}}
+ \@writefile{toc}{\contentsline {section}{\numberline {1}Foo}{1}\protected@file@percent }
+ \newlabel{sec:foo}{{1}{1}}
+ \newlabel{eq:foo}{{1}{1}}
+ \@writefile{toc}{\contentsline {section}{\numberline {2}Bar}{1}\protected@file@percent }
+ \newlabel{sec:bar}{{2}{1}}
+ \@writefile{toc}{\contentsline {section}{\numberline {3}Baz}{1}\protected@file@percent }
+ \newlabel{sec:baz}{{3}{1}}
+ \newlabel{itm:foo}{{1}{1}}
+ \newlabel{itm:bar}{{2}{1}}
+ \newlabel{itm:baz}{{3}{1}}
+ \@writefile{toc}{\contentsline {section}{\numberline {4}Qux}{1}\protected@file@percent }
+ \newlabel{sec:qux}{{4}{1}}
+ \newlabel{thm:qux}{{1}{1}}
+ "#,
+ "latex",
+ )?;
+ server.open_memory(
+ Url::parse("http://www.example.com/main.bib")?,
+ r#"
+ @article{foo,}
+
+ @string{bar = "bar"}
+ "#,
+ "bibtex",
+ )?;
-async fn run(query: &str) -> (TestBed, Vec<SymbolInformation>) {
- let mut test_bed = TestBedBuilder::new()
- .file(
- "foo.tex",
- indoc!(
- r#"
- \documentclass{article}
- \usepackage{caption}
- \usepackage{amsmath}
- \usepackage{amsthm}
- %
- \begin{document}
- %
- \section{Foo}\label{sec:foo}
- %
- \begin{equation}\label{eq:foo}
- Foo
- \end{equation}
- %
- \section{Bar}\label{sec:bar}
- %
- \begin{figure}
- Bar
- \caption{Bar}
- \label{fig:bar}
- \end{figure}
- %
- \section{Baz}\label{sec:baz}
- %
- \begin{enumerate}
- \item\label{itm:foo} Foo
- \item\label{itm:bar} Bar
- \item\label{itm:baz} Baz
- \end{enumerate}
- %
- \section{Qux}\label{sec:qux}
- %
- \newtheorem{lemma}{Lemma}
- %
- \begin{lemma}[Qux]\label{thm:qux}
- Qux
- \end{lemma}
- %
- \end{document}
- "#
- ),
- )
- .file(
- "foo.aux",
- indoc!(
- r#"
- \relax
- \@writefile{lof}{\contentsline {figure}{\numberline {1}{\ignorespaces Bar\relax }}{1}\protected@file@percent }
- \providecommand*\caption@xref[2]{\@setref\relax\@undefined{#1}}
- \newlabel{fig:bar}{{1}{1}}
- \@writefile{toc}{\contentsline {section}{\numberline {1}Foo}{1}\protected@file@percent }
- \newlabel{sec:foo}{{1}{1}}
- \newlabel{eq:foo}{{1}{1}}
- \@writefile{toc}{\contentsline {section}{\numberline {2}Bar}{1}\protected@file@percent }
- \newlabel{sec:bar}{{2}{1}}
- \@writefile{toc}{\contentsline {section}{\numberline {3}Baz}{1}\protected@file@percent }
- \newlabel{sec:baz}{{3}{1}}
- \newlabel{itm:foo}{{1}{1}}
- \newlabel{itm:bar}{{2}{1}}
- \newlabel{itm:baz}{{3}{1}}
- \@writefile{toc}{\contentsline {section}{\numberline {4}Qux}{1}\protected@file@percent }
- \newlabel{sec:qux}{{4}{1}}
- \newlabel{thm:qux}{{1}{1}}
- "#
- ),
- )
- .file(
- "bar.bib",
- indoc!(
- r#"
- @article{foo,}
- %
- @string{bar = "bar"}
- "#
- ),
- )
- .build()
- .await;
- test_bed.spawn();
- test_bed.initialize(PULL_CAPABILITIES.clone()).await;
- test_bed.open("foo.tex").await;
- test_bed.open("foo.aux").await;
- test_bed.open("bar.bib").await;
-
- let params = WorkspaceSymbolParams {
- query: query.into(),
- ..WorkspaceSymbolParams::default()
- };
- let actual_symbols = test_bed.client.workspace_symbol(params).await.unwrap();
-
- test_bed.shutdown().await;
-
- (test_bed, actual_symbols)
+ server.find_workspace_symbols(query)
}
-#[tokio::test]
-async fn filter_type_section() {
- let (test_bed, actual_symbols) = run("section").await;
- assert_eq!(actual_symbols.len(), 4);
- verify_symbol_info(
- &actual_symbols[0],
- &test_bed,
- "foo.tex",
- "1 Foo",
- 7,
- 0,
- 13,
- 0,
- );
- verify_symbol_info(
- &actual_symbols[1],
- &test_bed,
- "foo.tex",
- "2 Bar",
- 13,
- 0,
- 21,
- 0,
- );
- verify_symbol_info(
- &actual_symbols[2],
- &test_bed,
- "foo.tex",
- "3 Baz",
- 21,
- 0,
- 29,
- 0,
- );
- verify_symbol_info(
- &actual_symbols[3],
- &test_bed,
- "foo.tex",
- "4 Qux",
- 29,
- 0,
- 37,
- 0,
- );
+#[test]
+fn test_filter_type_section() -> Result<()> {
+ assert_json_snapshot!(run("section")?);
+ Ok(())
}
-#[tokio::test]
-async fn filter_type_figure() {
- let (test_bed, actual_symbols) = run("figure").await;
- assert_eq!(actual_symbols.len(), 1);
- verify_symbol_info(
- &actual_symbols[0],
- &test_bed,
- "foo.tex",
- "Figure 1: Bar",
- 15,
- 0,
- 19,
- 12,
- );
+#[test]
+fn test_filter_type_figure() -> Result<()> {
+ assert_json_snapshot!(run("figure")?);
+ Ok(())
}
-#[tokio::test]
-async fn filter_type_item() {
- let (test_bed, actual_symbols) = run("item").await;
- assert_eq!(actual_symbols.len(), 3);
- verify_symbol_info(&actual_symbols[0], &test_bed, "foo.tex", "1", 24, 4, 25, 4);
- verify_symbol_info(&actual_symbols[1], &test_bed, "foo.tex", "2", 25, 4, 26, 4);
- verify_symbol_info(&actual_symbols[2], &test_bed, "foo.tex", "3", 26, 4, 27, 0);
+#[test]
+fn test_filter_type_item() -> Result<()> {
+ assert_json_snapshot!(run("item")?);
+ Ok(())
}
-#[tokio::test]
-async fn filter_type_math() {
- let (test_bed, actual_symbols) = run("math").await;
- assert_eq!(actual_symbols.len(), 2);
- verify_symbol_info(
- &actual_symbols[0],
- &test_bed,
- "foo.tex",
- "Equation (1)",
- 9,
- 0,
- 11,
- 14,
- );
- verify_symbol_info(
- &actual_symbols[1],
- &test_bed,
- "foo.tex",
- "Lemma 1 (Qux)",
- 33,
- 0,
- 35,
- 11,
- );
+#[test]
+fn test_filter_type_math() -> Result<()> {
+ assert_json_snapshot!(run("math")?);
+ Ok(())
}
-#[tokio::test]
-async fn filter_bibtex() {
- let (test_bed, actual_symbols) = run("bibtex").await;
- assert_eq!(actual_symbols.len(), 2);
- verify_symbol_info(&actual_symbols[0], &test_bed, "bar.bib", "foo", 0, 0, 0, 14);
- verify_symbol_info(&actual_symbols[1], &test_bed, "bar.bib", "bar", 2, 0, 2, 20);
+#[test]
+fn test_filter_bibtex() -> Result<()> {
+ assert_json_snapshot!(run("bibtex")?);
+ Ok(())
}