From 9c99f4577030ba9e78a0290245633c24ca1ec0bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20F=C3=B6rster?= Date: Thu, 9 May 2019 18:26:46 +0200 Subject: [PATCH] Provide completion for citations --- Cargo.lock | 1 + Cargo.toml | 1 + src/completion/factory.rs | 19 ++++++- src/completion/latex/citation.rs | 85 ++++++++++++++++++++++++++++ src/completion/latex/mod.rs | 1 + src/completion/latex/user_command.rs | 5 +- src/completion/mod.rs | 2 + 7 files changed, 110 insertions(+), 4 deletions(-) create mode 100644 src/completion/latex/citation.rs diff --git a/Cargo.lock b/Cargo.lock index 77396828b..f02b2744e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -841,6 +841,7 @@ dependencies = [ "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-stdin-stdout 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "url_serde 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "walkdir 2.2.7 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/Cargo.toml b/Cargo.toml index 45c903e1e..21d867c31 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,6 +28,7 @@ clap = "2.33" indoc = "0.3.1" regex = "1.1.6" url = "1.7.2" +url_serde = "0.2.0" path-clean = "0.1.0" itertools = "0.8.0" walkdir = "2" diff --git a/src/completion/factory.rs b/src/completion/factory.rs index fddfda8d8..1fc0a3cc7 100644 --- a/src/completion/factory.rs +++ b/src/completion/factory.rs @@ -1,4 +1,4 @@ -use lsp_types::{CompletionItem, CompletionItemKind, InsertTextFormat}; +use lsp_types::{CompletionItem, CompletionItemKind, InsertTextFormat, Uri}; use serde::{Deserialize, Serialize}; use std::borrow::Cow; use std::path::Path; @@ -20,7 +20,7 @@ impl LatexComponentId { } } -#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)] +#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] pub enum CompletionItemData { Snippet, Command, @@ -36,7 +36,11 @@ pub enum CompletionItemData { Class, EntryKind, FieldName, - Citation, + Citation { + #[serde(with = "url_serde")] + uri: Uri, + key: String, + }, CommandSymbol, ArgumentSymbol, } @@ -157,3 +161,12 @@ pub fn create_class(name: Cow<'static, str>) -> CompletionItem { ..CompletionItem::default() } } + +pub fn create_citation(uri: Uri, key: String) -> CompletionItem { + CompletionItem { + label: Cow::from(key.clone()), + kind: Some(CompletionItemKind::Field), + data: Some(CompletionItemData::Citation { uri, key }.into()), + ..CompletionItem::default() + } +} diff --git a/src/completion/latex/citation.rs b/src/completion/latex/citation.rs new file mode 100644 index 000000000..45c24cb24 --- /dev/null +++ b/src/completion/latex/citation.rs @@ -0,0 +1,85 @@ +use crate::completion::factory; +use crate::completion::latex::combinators::LatexCombinators; +use crate::feature::FeatureRequest; +use crate::syntax::bibtex::BibtexDeclaration; +use crate::syntax::latex::CITATION_COMMANDS; +use crate::workspace::SyntaxTree; +use lsp_types::{CompletionItem, CompletionParams}; + +pub struct LatexCitationCompletionProvider; + +impl LatexCitationCompletionProvider { + pub async fn execute(request: &FeatureRequest) -> Vec { + await!(LatexCombinators::argument( + request, + CITATION_COMMANDS, + 0, + async move |_| { + let mut items = Vec::new(); + for document in &request.related_documents { + if let SyntaxTree::Bibtex(tree) = &document.tree { + for declaration in &tree.root.children { + if let BibtexDeclaration::Entry(entry) = declaration { + if let Some(key) = &entry.key { + items.push(factory::create_citation( + document.uri.clone(), + key.text().to_owned(), + )); + } + } + } + } + } + items + } + )) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::completion::latex::data::types::LatexComponentDatabase; + use crate::feature::FeatureSpec; + use crate::test_feature; + use lsp_types::Position; + + #[test] + fn test_inside_cite() { + let items = test_feature!( + LatexCitationCompletionProvider, + FeatureSpec { + files: vec![ + FeatureSpec::file("foo.tex", "\\addbibresource{bar.bib}\n\\cite{}"), + FeatureSpec::file("bar.bib", "@article{foo,}"), + FeatureSpec::file("baz.bib", "@article{bar,}") + ], + main_file: "foo.tex", + position: Position::new(1, 6), + new_name: "", + component_database: LatexComponentDatabase::default(), + } + ); + assert_eq!(items.len(), 1); + assert_eq!("foo", items[0].label); + } + + #[test] + fn test_outside_cite() { + let items = test_feature!( + LatexCitationCompletionProvider, + FeatureSpec { + files: vec![ + FeatureSpec::file("foo.tex", "\\addbibresource{bar.bib}\n\\cite{}"), + FeatureSpec::file("bar.bib", "@article{foo,}"), + FeatureSpec::file("baz.bib", "@article{bar,}") + ], + main_file: "foo.tex", + position: Position::new(1, 7), + new_name: "", + component_database: LatexComponentDatabase::default(), + } + ); + assert_eq!(items, Vec::new()); + } +} diff --git a/src/completion/latex/mod.rs b/src/completion/latex/mod.rs index 666584857..33ce47627 100644 --- a/src/completion/latex/mod.rs +++ b/src/completion/latex/mod.rs @@ -1,4 +1,5 @@ pub mod begin_command; +pub mod citation; pub mod color; pub mod color_model; mod combinators; diff --git a/src/completion/latex/user_command.rs b/src/completion/latex/user_command.rs index 3b7571d55..220402efd 100644 --- a/src/completion/latex/user_command.rs +++ b/src/completion/latex/user_command.rs @@ -28,7 +28,10 @@ impl LatexUserCommandCompletionProvider { .map(|command| &command.name.text()[1..]) .unique() .map(|name| { - factory::create_command(Cow::from(name.to_owned()), &LatexComponentId::Unknown) + factory::create_command( + Cow::from(name.to_owned()), + &LatexComponentId::Unknown, + ) }) .for_each(|item| items.push(item)); } diff --git a/src/completion/mod.rs b/src/completion/mod.rs index 6f72f9bc4..1d361dafa 100644 --- a/src/completion/mod.rs +++ b/src/completion/mod.rs @@ -3,6 +3,7 @@ pub mod latex; mod quality; use self::latex::begin_command::LatexBeginCommandCompletionProvider; +use self::latex::citation::LatexCitationCompletionProvider; use self::latex::color::LatexColorCompletionProvider; use self::latex::color_model::LatexColorModelCompletionProvider; use self::latex::include::LatexIncludeCompletionProvider; @@ -36,6 +37,7 @@ impl CompletionProvider { LatexColorCompletionProvider, LatexColorModelCompletionProvider, LatexLabelCompletionProvider, + LatexCitationCompletionProvider, LatexIncludeCompletionProvider, LatexBeginCommandCompletionProvider, LatexTikzCommandCompletionProvider,