diff --git a/.gitignore b/.gitignore index 2ac5e62ae..002ba3a0f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ target/ **/*.rs.bk /.idea +tarpaulin-report.html diff --git a/scenarios/completion/citation/bar.bib b/scenarios/completion/citation/bar.bib new file mode 100644 index 000000000..af2d2d9e6 --- /dev/null +++ b/scenarios/completion/citation/bar.bib @@ -0,0 +1,3 @@ +@article{foo,} + +@article{bar,} diff --git a/scenarios/completion/citation/baz/qux.bib b/scenarios/completion/citation/baz/qux.bib new file mode 100644 index 000000000..fee1d268a --- /dev/null +++ b/scenarios/completion/citation/baz/qux.bib @@ -0,0 +1,3 @@ +@article{baz,} + +@comment{qux,} \ No newline at end of file diff --git a/scenarios/completion/citation/foo.tex b/scenarios/completion/citation/foo.tex new file mode 100644 index 000000000..df8cd9249 --- /dev/null +++ b/scenarios/completion/citation/foo.tex @@ -0,0 +1,4 @@ +\addbibresource{bar.bib} +\addbibresource{baz/qux.bib} + +\cite{} \ No newline at end of file diff --git a/scenarios/completion/color/foo.tex b/scenarios/completion/color/foo.tex new file mode 100644 index 000000000..a3c2db02d --- /dev/null +++ b/scenarios/completion/color/foo.tex @@ -0,0 +1,2 @@ +\color{bla} +\definecolor{foo}{} \ No newline at end of file diff --git a/scenarios/completion/entry_type/foo.bib b/scenarios/completion/entry_type/foo.bib new file mode 100644 index 000000000..cf2fb0a5e --- /dev/null +++ b/scenarios/completion/entry_type/foo.bib @@ -0,0 +1,4 @@ +@artic +@preamble +@string +@comment \ No newline at end of file diff --git a/scenarios/completion/field_name/foo.bib b/scenarios/completion/field_name/foo.bib new file mode 100644 index 000000000..a225a6723 --- /dev/null +++ b/scenarios/completion/field_name/foo.bib @@ -0,0 +1,3 @@ +@article{foo, + aut +} \ No newline at end of file diff --git a/scenarios/completion/include/bar/bar.tex b/scenarios/completion/include/bar/bar.tex new file mode 100644 index 000000000..e69de29bb diff --git a/scenarios/completion/include/bar/baz.tex b/scenarios/completion/include/bar/baz.tex new file mode 100644 index 000000000..10df9337f --- /dev/null +++ b/scenarios/completion/include/bar/baz.tex @@ -0,0 +1,3 @@ +\bibliography{./} +\includegraphics{} +\includesvg{} \ No newline at end of file diff --git a/scenarios/completion/include/bar/foo.bib b/scenarios/completion/include/bar/foo.bib new file mode 100644 index 000000000..e69de29bb diff --git a/scenarios/completion/include/bar/image1.png b/scenarios/completion/include/bar/image1.png new file mode 100644 index 000000000..e69de29bb diff --git a/scenarios/completion/include/bar/image2.jpg b/scenarios/completion/include/bar/image2.jpg new file mode 100644 index 000000000..e69de29bb diff --git a/scenarios/completion/include/bar/image3.svg b/scenarios/completion/include/bar/image3.svg new file mode 100644 index 000000000..e69de29bb diff --git a/scenarios/completion/include/foo.tex b/scenarios/completion/include/foo.tex new file mode 100644 index 000000000..8753c6ff0 --- /dev/null +++ b/scenarios/completion/include/foo.tex @@ -0,0 +1,2 @@ +\include{} +\input{bar/} \ No newline at end of file diff --git a/scenarios/completion/include/qux.tex b/scenarios/completion/include/qux.tex new file mode 100644 index 000000000..e69de29bb diff --git a/scenarios/completion/kernel/foo.bib b/scenarios/completion/kernel/foo.bib new file mode 100644 index 000000000..6b0256726 --- /dev/null +++ b/scenarios/completion/kernel/foo.bib @@ -0,0 +1,3 @@ +@article{foo, + title = {\LaT} +} \ No newline at end of file diff --git a/scenarios/completion/kernel/foo.tex b/scenarios/completion/kernel/foo.tex new file mode 100644 index 000000000..852b9334e --- /dev/null +++ b/scenarios/completion/kernel/foo.tex @@ -0,0 +1,7 @@ +\documentclass{article} + +\usep + +\begin{doc} + +\end{doc} \ No newline at end of file diff --git a/scenarios/definition_label/bar.tex b/scenarios/completion/label/bar.tex similarity index 100% rename from scenarios/definition_label/bar.tex rename to scenarios/completion/label/bar.tex diff --git a/scenarios/completion/label/baz.tex b/scenarios/completion/label/baz.tex new file mode 100644 index 000000000..3a64d3df5 --- /dev/null +++ b/scenarios/completion/label/baz.tex @@ -0,0 +1 @@ +\label{baz} diff --git a/scenarios/completion/label/foo.tex b/scenarios/completion/label/foo.tex new file mode 100644 index 000000000..dee8dd610 --- /dev/null +++ b/scenarios/completion/label/foo.tex @@ -0,0 +1,6 @@ +\include{bar} +\input{baz.tex} + +\label{foo} + +\ref{} \ No newline at end of file diff --git a/scenarios/completion/pgf_library/foo.tex b/scenarios/completion/pgf_library/foo.tex new file mode 100644 index 000000000..fe87a0d01 --- /dev/null +++ b/scenarios/completion/pgf_library/foo.tex @@ -0,0 +1 @@ +\usepgflibrary{arr} diff --git a/scenarios/completion/symbol/foo.tex b/scenarios/completion/symbol/foo.tex new file mode 100644 index 000000000..f7d5371cf --- /dev/null +++ b/scenarios/completion/symbol/foo.tex @@ -0,0 +1,2 @@ +\vareps +\mathbb{} \ No newline at end of file diff --git a/scenarios/completion/tikz_library/foo.tex b/scenarios/completion/tikz_library/foo.tex new file mode 100644 index 000000000..1f88a3b16 --- /dev/null +++ b/scenarios/completion/tikz_library/foo.tex @@ -0,0 +1 @@ +\usetikzlibrary{arr} \ No newline at end of file diff --git a/scenarios/completion/user/foo.tex b/scenarios/completion/user/foo.tex new file mode 100644 index 000000000..a12e53f2a --- /dev/null +++ b/scenarios/completion/user/foo.tex @@ -0,0 +1,9 @@ +\foo + +\fo + +\begin{foo} + +\end{foo} + +\begin{fo} \ No newline at end of file diff --git a/scenarios/definition_citation/bar.bib b/scenarios/definition/citation/bar.bib similarity index 100% rename from scenarios/definition_citation/bar.bib rename to scenarios/definition/citation/bar.bib diff --git a/scenarios/definition_citation/foo.bib b/scenarios/definition/citation/foo.bib similarity index 100% rename from scenarios/definition_citation/foo.bib rename to scenarios/definition/citation/foo.bib diff --git a/scenarios/definition_citation/foo.tex b/scenarios/definition/citation/foo.tex similarity index 100% rename from scenarios/definition_citation/foo.tex rename to scenarios/definition/citation/foo.tex diff --git a/scenarios/definition_label/baz.tex b/scenarios/definition/label/bar.tex similarity index 100% rename from scenarios/definition_label/baz.tex rename to scenarios/definition/label/bar.tex diff --git a/scenarios/definition/label/baz.tex b/scenarios/definition/label/baz.tex new file mode 100644 index 000000000..d14f045aa --- /dev/null +++ b/scenarios/definition/label/baz.tex @@ -0,0 +1 @@ +\label{bar} diff --git a/scenarios/definition_label/foo.tex b/scenarios/definition/label/foo.tex similarity index 100% rename from scenarios/definition_label/foo.tex rename to scenarios/definition/label/foo.tex diff --git a/src/completion/latex/citation.rs b/src/completion/latex/citation.rs index 4fd5eb3b4..fe3522fa4 100644 --- a/src/completion/latex/citation.rs +++ b/src/completion/latex/citation.rs @@ -18,8 +18,10 @@ impl LatexCitationCompletionProvider { for document in &request.related_documents { if let SyntaxTree::Bibtex(tree) = &document.tree { for entry in &tree.entries() { - if let Some(key) = &entry.key { - items.push(factory::create_citation(entry, key.text())); + if !entry.is_comment() { + if let Some(key) = &entry.key { + items.push(factory::create_citation(entry, key.text())); + } } } } diff --git a/src/completion/latex/pgf_library.rs b/src/completion/latex/pgf_library.rs index 2be841c79..a4fb64dc0 100644 --- a/src/completion/latex/pgf_library.rs +++ b/src/completion/latex/pgf_library.rs @@ -24,7 +24,7 @@ impl LatexPgfLibraryCompletionProvider { const COMMANDS: &[&str] = &["\\usepgflibrary"]; -const LIBRARIES: &[&str] = &[ +static LIBRARIES: &[&str] = &[ "arrows", "arrows.meta", "arrows.spaced", diff --git a/src/completion/latex/tikz_command.rs b/src/completion/latex/tikz_command.rs index 58f221f99..42aaccaaf 100644 --- a/src/completion/latex/tikz_command.rs +++ b/src/completion/latex/tikz_command.rs @@ -28,7 +28,7 @@ impl LatexTikzCommandCompletionProvider { } } -const COMMANDS: &[&str] = &[ +static COMMANDS: &[&str] = &[ "afterdecoration", "anchor", "anchorborder", diff --git a/src/completion/latex/tikz_library.rs b/src/completion/latex/tikz_library.rs index df7f2160e..1d4ab2ef7 100644 --- a/src/completion/latex/tikz_library.rs +++ b/src/completion/latex/tikz_library.rs @@ -24,7 +24,7 @@ impl LatexTikzLibraryCompletionProvider { const COMMANDS: &[&str] = &["\\usetikzlibrary"]; -const LIBRARIES: &[&str] = &[ +static LIBRARIES: &[&str] = &[ "3d", "angles", "arrows", diff --git a/src/data/bibtex_entry_type.json b/src/data/bibtex_entry_type.json index 7c6e8ed81..f6d7e7699 100644 --- a/src/data/bibtex_entry_type.json +++ b/src/data/bibtex_entry_type.json @@ -7,6 +7,10 @@ "name": "string", "documentation": null }, + { + "name": "comment", + "documentation": null + }, { "name": "article", "documentation": "An article in a journal, magazine, newspaper, or other periodical which forms a \n self-contained unit with its own title. The title of the periodical is given in the \n journaltitle field. If the issue has its own title in addition to the main title of \n the periodical, it goes in the issuetitle field. Note that editor and related \n fields refer to the journal while translator and related fields refer to the article.\n\nRequired fields: `author`, `title`, `journaltitle`, `year/date`" diff --git a/tests/common.rs b/tests/common.rs index ffcef169d..74eeed059 100644 --- a/tests/common.rs +++ b/tests/common.rs @@ -7,12 +7,12 @@ use futures::prelude::*; use jsonrpc::client::FutureResult; use jsonrpc::server::EventHandler; use lsp_types::*; +use std::fs::remove_dir; use std::path::PathBuf; use std::sync::Arc; use tempfile::TempDir; use texlab::client::LspClient; use texlab::server::LatexLspServer; -use std::fs::remove_dir; #[derive(Debug, Default)] pub struct LspClientMock { @@ -80,15 +80,22 @@ impl Scenario { Uri::from_file_path(path).unwrap() } - pub async fn open(&self, name: &'static str, language_id: &'static str) { + pub async fn open(&self, name: &'static str) { let mut path = self.directory.path().to_owned(); path.push(name); + let text = std::fs::read_to_string(path).unwrap(); + let language_id = if name.ends_with(".tex") { + "latex" + } else { + "bibtex" + }; + self.server.did_open(DidOpenTextDocumentParams { text_document: TextDocumentItem { uri: self.uri(name), version: 0, language_id: language_id.to_owned(), - text: std::fs::read_to_string(path).unwrap(), + text, }, }) } diff --git a/tests/completion.rs b/tests/completion.rs new file mode 100644 index 000000000..3d2bd93b3 --- /dev/null +++ b/tests/completion.rs @@ -0,0 +1,205 @@ +#![feature(await_macro, async_await)] + +mod common; + +use crate::common::Scenario; +use futures::executor::block_on; +use itertools::Itertools; +use lsp_types::*; + +async fn run(scenario: &'static str, file: &'static str, position: Position) -> Vec { + let scenario = format!("completion/{}", scenario); + let scenario = await!(Scenario::new(&scenario)); + await!(scenario.open(file)); + + let params = CompletionParams { + text_document: TextDocumentIdentifier::new(scenario.uri(file)), + position, + context: None, + }; + await!(scenario.server.completion(params)) + .unwrap() + .items + .into_iter() + .map(|item| item.label.into_owned()) + .sorted() + .collect() +} + +#[test] +fn test_kernel_command() { + block_on(async move { + let items = await!(run("kernel", "foo.tex", Position::new(2, 5))); + assert_eq!(items.iter().any(|item| item == "usepackage"), true); + }); +} + +#[test] +fn test_kernel_command_bibtex() { + block_on(async move { + let items = await!(run("kernel", "foo.bib", Position::new(1, 17))); + assert_eq!(items.iter().any(|item| item == "LaTeX"), true); + }); +} + +#[test] +fn test_kernel_environment() { + block_on(async move { + let items = await!(run("kernel", "foo.tex", Position::new(4, 10))); + assert_eq!(items.iter().any(|item| item == "document"), true); + }); +} + +#[test] +fn test_user_command() { + block_on(async move { + let items = await!(run("user", "foo.tex", Position::new(2, 3))); + assert_eq!(items.iter().all(|item| item != "fo"), true); + assert_eq!(items.iter().any(|item| item == "foo"), true); + }); +} + +#[test] +fn test_label() { + block_on(async move { + let items = await!(run("label", "foo.tex", Position::new(5, 5))); + assert_eq!(items, vec!["bar", "baz", "foo"]); + }); +} + +#[test] +fn test_citation() { + block_on(async move { + let items = await!(run("citation", "foo.tex", Position::new(3, 6))); + assert_eq!(items, vec!["bar", "baz", "foo"]); + }); +} + +#[test] +fn test_symbol_command_kernel() { + block_on(async move { + let items = await!(run("symbol", "foo.tex", Position::new(0, 1))); + assert_eq!(items.iter().any(|item| item == "varepsilon"), true); + }); +} + +#[test] +fn test_symbol_argument() { + block_on(async move { + let items = await!(run("symbol", "foo.tex", Position::new(1, 8))); + assert_eq!(items.len(), 26); + assert_eq!(items[0], "A"); + }); +} + +#[test] +fn test_color() { + block_on(async move { + let items = await!(run("color", "foo.tex", Position::new(0, 10))); + assert_eq!(items.iter().any(|item| item == "black"), true); + }); +} + +#[test] +fn test_color_model() { + block_on(async move { + let items = await!(run("color", "foo.tex", Position::new(1, 18))); + assert_eq!(items.iter().any(|item| item == "rgb"), true); + }); +} + +#[test] +fn test_include_top_level() { + block_on(async move { + let items = await!(run("include", "foo.tex", Position::new(0, 9))); + assert_eq!(items, vec!["bar", "foo", "qux"]); + }); +} + +#[test] +fn test_include_directory() { + block_on(async move { + let items = await!(run("include", "foo.tex", Position::new(1, 11))); + assert_eq!(items, vec!["bar.tex", "baz.tex"]); + }); +} + +#[test] +fn test_include_bibliography() { + block_on(async move { + let items = await!(run("include", "bar/baz.tex", Position::new(0, 16))); + assert_eq!(items, vec!["foo.bib"]); + }); +} + +#[test] +fn test_include_graphics() { + block_on(async move { + let items = await!(run("include", "bar/baz.tex", Position::new(1, 17))); + assert_eq!(items, vec!["image1.png", "image2.jpg"]); + }); +} + +#[test] +fn test_include_graphics_svg() { + block_on(async move { + let items = await!(run("include", "bar/baz.tex", Position::new(2, 12))); + assert_eq!(items, vec!["image3"]); + }); +} + +#[test] +fn test_pgf_library() { + block_on(async move { + let items = await!(run("pgf_library", "foo.tex", Position::new(0, 18))); + assert_eq!(items.iter().any(|item| item == "arrows"), true); + }); +} + +#[test] +fn test_tikz_library() { + block_on(async move { + let items = await!(run("tikz_library", "foo.tex", Position::new(0, 19))); + assert_eq!(items.iter().any(|item| item == "arrows"), true); + }); +} + +#[test] +fn test_entry_type() { + block_on(async move { + let items = await!(run("entry_type", "foo.bib", Position::new(0, 1))); + assert_eq!(items.iter().any(|item| item == "article"), true); + }); +} + +#[test] +fn test_entry_type_preamble() { + block_on(async move { + let items = await!(run("entry_type", "foo.bib", Position::new(1, 3))); + assert_eq!(items.iter().any(|item| item == "preamble"), true); + }); +} + +#[test] +fn test_entry_type_string() { + block_on(async move { + let items = await!(run("entry_type", "foo.bib", Position::new(2, 3))); + assert_eq!(items.iter().any(|item| item == "string"), true); + }); +} + +#[test] +fn test_entry_type_comment() { + block_on(async move { + let items = await!(run("entry_type", "foo.bib", Position::new(3, 3))); + assert_eq!(items.iter().any(|item| item == "comment"), true); + }); +} + +#[test] +fn test_field_name() { + block_on(async move { + let items = await!(run("field_name", "foo.bib", Position::new(1, 7))); + assert_eq!(items.iter().any(|item| item == "author"), true); + }); +} \ No newline at end of file diff --git a/tests/definition.rs b/tests/definition.rs index ac630eee3..a03fb645e 100644 --- a/tests/definition.rs +++ b/tests/definition.rs @@ -6,16 +6,24 @@ use crate::common::Scenario; use futures::executor::block_on; use lsp_types::*; +async fn run( + scenario: &'static str, + file: &'static str, + position: Position, +) -> (Scenario, Vec) { + let scenario = format!("definition/{}", scenario); + let scenario = await!(Scenario::new(&scenario)); + let identifier = TextDocumentIdentifier::new(scenario.uri(file)); + let params = TextDocumentPositionParams::new(identifier, position); + await!(scenario.open(file)); + let definitions = await!(scenario.server.definition(params)).unwrap(); + (scenario, definitions) +} + #[test] fn test_citation() { block_on(async move { - let scenario = await!(Scenario::new("definition_citation")); - let params = TextDocumentPositionParams::new( - TextDocumentIdentifier::new(scenario.uri("foo.tex")), - Position::new(5, 8), - ); - await!(scenario.open("foo.tex", "latex")); - let definitions = await!(scenario.server.definition(params)).unwrap(); + let (scenario, definitions) = await!(run("citation", "foo.tex", Position::new(5, 8))); assert_eq!( definitions, vec![Location::new( @@ -29,13 +37,7 @@ fn test_citation() { #[test] fn test_label() { block_on(async move { - let scenario = await!(Scenario::new("definition_label")); - let params = TextDocumentPositionParams::new( - TextDocumentIdentifier::new(scenario.uri("foo.tex")), - Position::new(8, 8), - ); - await!(scenario.open("foo.tex", "latex")); - let definitions = await!(scenario.server.definition(params)).unwrap(); + let (scenario, definitions) = await!(run("label", "foo.tex", Position::new(8, 8))); assert_eq!( definitions, vec![Location::new(