use anyhow::Result;
use insta::assert_json_snapshot;
use lsp_types::{
request::{Completion, ResolveCompletionItem},
ClientCapabilities, CompletionItem, CompletionList, CompletionParams, CompletionResponse,
CompletionTextEdit, Range,
};
use crate::lsp::{client::Client, fixture};
fn complete(fixture: &str) -> Result<Vec<CompletionItem>, anyhow::Error> {
let mut client = Client::spawn()?;
client.initialize(ClientCapabilities::default(), None)?;
let fixture = fixture::parse(fixture);
for file in fixture.files {
client.open(file.name, file.lang, file.text)?;
}
let range = fixture
.ranges
.values()
.next()
.and_then(|map| map.values().next())
.map_or_else(
|| {
let position = fixture.cursor.unwrap().position;
Range::new(position, position)
},
|file_range| file_range.range,
);
let actual_list = client
.request::<Completion>(CompletionParams {
text_document_position: fixture.cursor.unwrap().into_params(&client)?,
partial_result_params: Default::default(),
work_done_progress_params: Default::default(),
context: None,
})?
.map_or(CompletionList::default(), |actual| match actual {
CompletionResponse::List(list) => list,
CompletionResponse::Array(_) => unreachable!(),
});
for item in &actual_list.items {
if let Some(CompletionTextEdit::Edit(edit)) = item.text_edit.as_ref() {
assert_eq!(edit.range, range);
}
}
let actual_items = actual_list
.items
.into_iter()
.take(5)
.map(|item| client.request::<ResolveCompletionItem>(item))
.collect::<Result<Vec<_>>>()?;
client.shutdown()?;
Ok(actual_items)
}
macro_rules! assert_items {
($items:expr) => {
assert_json_snapshot!($items, {
"[].data" => "[data]",
"[].sortText" => "[sortText]",
"[].documentation" => "[documentation]",
"[].textEdit.range" => "[range]",
"[]" => insta::sorted_redaction()
});
};
}
#[test]
fn acronym_ref_simple() -> Result<()> {
assert_items!(complete(
r#"
%TEX main.tex
%SRC \newacronym[longplural={Frames per Second}]{fpsLabel}{FPS}{Frame per Second}
%SRC \acrshort{f}
%CUR ^
%1.1 ^"#
)?);
Ok(())
}
#[test]
fn acronym_ref_empty() -> Result<()> {
assert_items!(complete(
r#"
%TEX main.tex
%SRC \newacronym[longplural={Frames per Second}]{fpsLabel}{FPS}{Frame per Second}
%SRC \acrshort{}
%CUR ^"#
)?);
Ok(())
}
#[test]
fn acronym_ref_after_group() -> Result<()> {
let actual_items = complete(
r#"
%TEX main.tex
%SRC \newacronym[longplural={Frames per Second}]{fpsLabel}{FPS}{Frame per Second}
%SRC \acrshort{}
%CUR ^
%1.1 ^"#,
)?;
assert_eq!(actual_items, Vec::new());
Ok(())
}
#[test]
fn acronym_ref_open_brace() -> Result<()> {
assert_items!(complete(
r#"
%TEX main.tex
%SRC \newacronym[longplural={Frames per Second}]{fpsLabel}{FPS}{Frame per Second}
%SRC \acrshort{f
%CUR ^
%1.1 ^"#
)?);
Ok(())
}
#[test]
fn acronym_package_ref() -> Result<()> {
assert_items!(complete(
r#"
%TEX main.tex
%SRC \acrodef{fpsLabel}[FPS]{Frames per Second}
%SRC \ac{f
%CUR ^
%1.1 ^"#
)?);
Ok(())
}
#[test]
fn glossary_ref_simple() -> Result<()> {
assert_items!(complete(
r#"
%TEX main.tex
%SRC \newacronym[longplural={Frames per Second}]{fpsLabel}{FPS}{Frame per Second}
%SRC \gls{f}
%CUR ^
%1.1 ^"#
)?);
Ok(())
}
#[test]
fn glossary_ref_open_brace() -> Result<()> {
assert_items!(complete(
r#"
%TEX main.tex
%SRC \newacronym[longplural={Frames per Second}]{fpsLabel}{FPS}{Frame per Second}
%SRC \gls{f
%CUR ^
%1.1 ^"#
)?);
Ok(())
}
#[test]
fn argument_empty() -> Result<()> {
assert_items!(complete(
r#"
%TEX main.tex
%SRC \usepackage{amsfonts}
%SRC \mathbb{}
%CUR ^"#
)?);
Ok(())
}
#[test]
fn argument_word() -> Result<()> {
assert_items!(complete(
r#"
%TEX main.tex
%SRC \usepackage{amsfonts}
%SRC \mathbb{A}
%CUR ^
%1.1 ^"#
)?);
Ok(())
}
#[test]
fn argument_open_brace() -> Result<()> {
assert_items!(complete(
r#"
%TEX main.tex
%SRC \usepackage{amsfonts}
%SRC \mathbb{
%CUR ^
%SRC Test"#
)?);
Ok(())
}
#[test]
fn argument_open_brace_unrelated() -> Result<()> {
let actual_items = complete(
r#"
%TEX main.tex
%SRC \usepackage{amsfonts}
%SRC \mathbb{}{
%CUR ^
%SRC Test"#,
)?;
assert_eq!(actual_items, Vec::new());
Ok(())
}
#[test]
fn begin_environment_without_snippet_support() -> Result<()> {
assert_items!(complete(
r#"
%TEX main.tex
%SRC \beg
%CUR ^
%1.1 ^^^"#
)?);
Ok(())
}
#[test]
fn citation() -> Result<()> {
assert_items!(complete(
r#"
%TEX main.tex
%SRC \documentclass{article}
%SRC \bibliography{main}
%SRC \begin{document}
%SRC \cite{
%CUR ^
%SRC \end{document}
%BIB main.bib
%SRC @article{foo:2019,
%SRC author = {Foo Bar},
%SRC title = {Baz Qux},
%SRC year = {2019},
%SRC }
%SRC
%SRC @article{bar:2005,}"#
)?);
Ok(())
}
#[test]
fn citation_open_brace() -> Result<()> {
assert_items!(complete(
r#"
%TEX main.tex
%SRC \addbibresource{main.bib}
%SRC \cite{
%CUR ^
%BIB main.bib
%SRC @article{foo,}"#
)?);
Ok(())
}
#[test]
fn citation_open_brace_multiple() -> Result<()> {
assert_items!(complete(
r#"
%TEX main.tex
%SRC \addbibresource{main.bib}
%SRC \cite{foo,a
%CUR ^
%1.1 ^
%BIB main.bib
%SRC @article{foo,}"#
)?);
Ok(())
}
#[test]
fn citation_acronym() -> Result<()> {
assert_items!(complete(
r#"
%TEX main.tex
%SRC \addbibresource{main.bib}
%SRC \DeclareAcronym{foo}{cite={}}
%CUR ^
%BIB main.bib
%SRC @article{foo,}"#
)?);
Ok(())
}
#[test]
fn citation_after_brace() -> Result<()> {
let actual_items = complete(
r#"
%TEX main.tex
%SRC \documentclass{article}
%SRC \bibliography{main}
%SRC \begin{document}
%SRC \cite{}
%CUR ^
%SRC \end{document}
%BIB main.bib
%SRC @article{foo,}"#,
)?;
assert_eq!(actual_items, Vec::new());
Ok(())
}
#[test]
fn color_model_definition_simple() -> Result<()> {
assert_items!(complete(
r#"
%TEX main.tex
%SRC \definecolor{foo}{}
%CUR ^"#
)?);
Ok(())
}
#[test]
fn color_model_definition_open_brace() -> Result<()> {
assert_items!(complete(
r#"
%TEX main.tex
%SRC \definecolor{foo}{
%CUR ^"#
)?);
Ok(())
}
#[test]
fn color_model_definition_set_simple() -> Result<()> {
assert_items!(complete(
r#"
%TEX main.tex
%SRC \definecolorset{}
%CUR ^"#
)?);
Ok(())
}
#[test]
fn color_model_definition_set_open_brace() -> Result<()> {
assert_items!(complete(
r#"
%TEX main.tex
%SRC \definecolorset{
%CUR ^"#
)?);
Ok(())
}
#[test]
fn color_simple() -> Result<()> {
assert_items!(complete(
r#"
%TEX main.tex
%SRC \color{}
%CUR ^"#
)?);
Ok(())
}
#[test]
fn color_word() -> Result<()> {
assert_items!(complete(
r#"
%TEX main.tex
%SRC \color{re}
%CUR ^
%1.1 ^^"#
)?);
Ok(())
}
#[test]
fn color_open_brace() -> Result<()> {
assert_items!(complete(
r#"
%TEX main.tex
%SRC \color{
%CUR ^"#
)?);
Ok(())
}
#[test]
fn component_command_simple() -> Result<()> {
assert_items!(complete(
r#"
%TEX main.tex
%SRC \
%CUR ^"#
)?);
Ok(())
}
#[test]
fn component_command_simple_before() -> Result<()> {
let actual_items = complete(
r#"
%TEX main.tex
%SRC \
%CUR ^"#,
)?;
assert_eq!(actual_items, Vec::new());
Ok(())
}
#[test]
fn component_command_simple_package() -> Result<()> {
assert_items!(complete(
r#"
%TEX main.tex
%SRC \usepackage{lipsum}
%SRC \lips
%CUR ^
%1.1 ^^^^"#
)?);
Ok(())
}
#[test]
fn component_command_bibtex() -> Result<()> {
assert_items!(complete(
r#"
%BIB main.bib
%SRC @article{b,
%SRC c = {\LaT }
%CUR ^
%1.1 ^^^
%SRC }"#
)?);
Ok(())
}
#[test]
fn component_environment_simple() -> Result<()> {
assert_items!(complete(
r#"
%TEX main.tex
%SRC \begin{doc
%CUR ^
%1.1 ^^^"#
)?);
Ok(())
}
#[test]
fn component_environment_simple_end() -> Result<()> {
assert_items!(complete(
r#"
%TEX main.tex
%SRC \begin{document}
%SRC \end{
%CUR ^"#
)?);
Ok(())
}
#[test]
fn component_environment_class() -> Result<()> {
assert_items!(complete(
r#"
%TEX main.tex
%SRC \documentclass{article}
%SRC \begin{thein}
%CUR ^
%1.1 ^^^^^"#
)?);
Ok(())
}
#[test]
fn component_environment_command_definition() -> Result<()> {
assert_items!(complete(
r#"
%TEX main.tex
%SRC \newcommand{\foo}{\begin{doc}
%CUR ^
%1.1 ^^^"#
)?);
Ok(())
}
#[test]
fn entry_type_at_empty() -> Result<()> {
assert_items!(complete(
r#"
%BIB main.bib
%SRC @
%CUR ^"#
)?);
Ok(())
}
#[test]
fn entry_type_before_preamble() -> Result<()> {
assert_items!(complete(
r#"
%BIB main.bib
%SRC @preamble
%CUR ^
%1.1 ^^^^^^^^"#
)?);
Ok(())
}
#[test]
fn entry_type_before_string() -> Result<()> {
assert_items!(complete(
r#"
%BIB main.bib
%SRC @string
%CUR ^
%1.1 ^^^^^^"#
)?);
Ok(())
}
#[test]
fn entry_type_before_article() -> Result<()> {
assert_items!(complete(
r#"
%BIB main.bib
%SRC @article
%CUR ^
%1.1 ^^^^^^^"#
)?);
Ok(())
}
#[test]
fn entry_type_after_preamble() -> Result<()> {
assert_items!(complete(
r#"
%BIB main.bib
%SRC @preamble{
%CUR ^
%1.1 ^^^^^^^^"#
)?);
Ok(())
}
#[test]
fn entry_type_after_string() -> Result<()> {
assert_items!(complete(
r#"
%BIB main.bib
%SRC @string{
%CUR ^
%1.1 ^^^^^^"#
)?);
Ok(())
}
#[test]
fn entry_type_complete_entry() -> Result<()> {
assert_items!(complete(
r#"
%BIB main.bib
%SRC @article{foo, author = {foo}}
%CUR ^
%1.1 ^^^^^^^"#
)?);
Ok(())
}
#[test]
fn field_empty_entry_open() -> Result<()> {
assert_items!(complete(
r#"
%BIB main.bib
%SRC @article{foo,
%CUR ^"#
)?);
Ok(())
}
#[test]
fn field_empty_entry_closed() -> Result<()> {
assert_items!(complete(
r#"
%BIB main.bib
%SRC @article{foo,}
%CUR ^"#
)?);
Ok(())
}
#[test]
fn field_entry_field_name() -> Result<()> {
assert_items!(complete(
r#"
%BIB main.bib
%SRC @article{foo, a
%CUR ^
%1.1 ^"#
)?);
Ok(())
}
#[test]
fn field_entry_two_fields_name_open() -> Result<()> {
assert_items!(complete(
r#"
%BIB main.bib
%SRC @article{foo, author = bar, edit
%CUR ^
%1.1 ^^^^"#
)?);
Ok(())
}
#[test]
fn field_entry_two_fields_name_closed() -> Result<()> {
assert_items!(complete(
r#"
%BIB main.bib
%SRC @article{foo, author = bar, edit}
%CUR ^
%1.1 ^^^^"#
)?);
Ok(())
}
#[test]
fn import_package_open_brace() -> Result<()> {
assert_items!(complete(
r#"
%TEX main.tex
%SRC \usepackage{lips
%CUR ^
%1.1 ^^^^"#
)?);
Ok(())
}
#[test]
fn import_package_closed_brace() -> Result<()> {
assert_items!(complete(
r#"
%TEX main.tex
%SRC \usepackage{lips}
%CUR ^
%1.1 ^^^^"#
)?);
Ok(())
}
#[test]
fn import_class_open_brace() -> Result<()> {
assert_items!(complete(
r#"
%TEX main.tex
%SRC \documentclass{art \foo
%CUR ^
%1.1 ^^^"#
)?);
Ok(())
}
#[test]
fn import_class_closed_brace() -> Result<()> {
assert_items!(complete(
r#"
%TEX main.tex
%SRC \documentclass{art}
%CUR ^
%1.1 ^^^"#
)?);
Ok(())
}
#[test]
fn label() -> Result<()> {
assert_items!(complete(
r#"
%TEX foo.tex
%SRC \documentclass{article}
%SRC
%SRC \usepackage{amsmath}
%SRC \usepackage{caption}
%SRC \usepackage{amsthm}
%SRC \newtheorem{lemma}{Lemma}
%SRC
%SRC \begin{document}
%SRC
%SRC \section{Foo}%
%SRC \label{sec:foo}
%SRC
%SRC \begin{equation}%
%SRC \label{eq:foo}
%SRC 1 + 1 = 2
%SRC \end{equation}
%SRC
%SRC \begin{equation}%
%SRC \label{eq:bar}
%SRC 1 + 1 = 2
%SRC \end{equation}
%SRC
%SRC \begin{figure}%
%SRC \LaTeX{}
%SRC \caption{Baz}%
%SRC \label{fig:baz}
%SRC \end{figure}
%SRC
%SRC \begin{lemma}%
%SRC \label{thm:foo}
%SRC 1 + 1 = 2
%SRC \end{lemma}
%SRC
%SRC \include{bar}
%SRC
%SRC \end{document}
%TEX bar.tex
%SRC \section{Bar}%
%SRC \label{sec:bar}
%SRC
%SRC Lorem ipsum dolor sit amet.
%SRC \ref{}
%CUR ^
%TEX foo.aux
%SRC \relax
%SRC \@writefile{lof}{\contentsline {figure}{\numberline {1}{\ignorespaces Baz\relax }}{1}\protected@file@percent }
%SRC \providecommand*\caption@xref[2]{\@setref\relax\@undefined{#1}}
%SRC \newlabel{fig:baz}{{1}{1}}
%SRC \@writefile{toc}{\contentsline {section}{\numberline {1}Foo}{1}\protected@file@percent }
%SRC \newlabel{sec:foo}{{1}{1}}
%SRC \newlabel{eq:foo}{{1}{1}}
%SRC \newlabel{eq:bar}{{2}{1}}
%SRC \newlabel{thm:foo}{{1}{1}}
%SRC \@input{bar.aux}"#
)?);
Ok(())
}
#[test]
fn theorem_begin() -> Result<()> {
assert_items!(complete(
r#"
%TEX main.tex
%SRC \newtheorem{lemma}{Lemma}
%SRC \begin{lem
%CUR ^
%1.1 ^^^"#
)?);
Ok(())
}
#[test]
fn theorem_end() -> Result<()> {
assert_items!(complete(
r#"
%TEX main.tex
%SRC \newtheorem{lemma}{Lemma}
%SRC \begin{}
%SRC \end{lem
%CUR ^
%1.1 ^^^"#
)?);
Ok(())
}
#[test]
fn tikz_library_open_brace() -> Result<()> {
assert_items!(complete(
r#"
%TEX main.tex
%SRC \usepgflibrary{
%CUR ^"#
)?);
Ok(())
}
#[test]
fn tikz_library_closed_brace() -> Result<()> {
assert_items!(complete(
r#"
%TEX main.tex
%SRC \usepgflibrary{}
%CUR ^"#
)?);
Ok(())
}
#[test]
fn test_user_command() -> Result<()> {
assert_items!(complete(
r#"
%TEX main.tex
%SRC \foobar
%SRC \fooba
%CUR ^
%1.1 ^^^^^
%SRC \begin{foo}
%SRC \end{foo}
%SRC \begin{fo}
"#
)?);
Ok(())
}
#[test]
fn test_user_environment() -> Result<()> {
assert_items!(complete(
r#"
%TEX main.tex
%SRC \foobar
%SRC \fooba
%SRC \begin{foo}
%SRC \end{foo}
%SRC \begin{fo}
%CUR ^
%1.1 ^^
"#
)?);
Ok(())
}