diff options
Diffstat (limited to 'support/texlab/crates/completion/src/providers/citations.rs')
-rw-r--r-- | support/texlab/crates/completion/src/providers/citations.rs | 71 |
1 files changed, 71 insertions, 0 deletions
diff --git a/support/texlab/crates/completion/src/providers/citations.rs b/support/texlab/crates/completion/src/providers/citations.rs new file mode 100644 index 0000000000..631a899203 --- /dev/null +++ b/support/texlab/crates/completion/src/providers/citations.rs @@ -0,0 +1,71 @@ +use base_db::semantics::Span; +use rayon::prelude::{IntoParallelRefIterator, ParallelExtend, ParallelIterator}; +use rowan::ast::AstNode; +use syntax::latex; + +use crate::{ + util::{find_curly_group_word_list, CompletionBuilder}, + CitationData, CompletionItem, CompletionItemData, CompletionParams, +}; + +pub fn complete_citations<'a>( + params: &'a CompletionParams<'a>, + builder: &mut CompletionBuilder<'a>, +) -> Option<()> { + let cursor = find_citation(params)?; + + for document in ¶ms.feature.project.documents { + if let Some(data) = document.data.as_bib() { + let items = data.semantics.entries.par_iter().filter_map(|entry| { + let score = builder.matcher.score(&entry.name.text, &cursor.text)?; + let data = CompletionItemData::Citation(CitationData { document, entry }); + Some(CompletionItem::new_simple(score, cursor.range, data)) + }); + + builder.items.par_extend(items); + } + } + + Some(()) +} + +fn find_citation(params: &CompletionParams) -> Option<Span> { + find_citation_command(params).or_else(|| find_citation_acronym(params)) +} + +fn find_citation_command(params: &CompletionParams) -> Option<Span> { + let (span, group) = find_curly_group_word_list(params)?; + latex::Citation::cast(group.syntax().parent()?)?; + Some(span) +} + +fn find_citation_acronym(params: &CompletionParams) -> Option<Span> { + let offset = params.offset; + let data = params.feature.document.data.as_tex()?; + let root = data.root_node(); + let tokens = root.token_at_offset(offset); + let token = tokens + .clone() + .find(|token| token.kind() == latex::WORD) + .or_else(|| tokens.left_biased())?; + + let span = if token.kind() == latex::WORD { + let name = latex::Text::cast(token.parent()?)?; + Span::new(token.text().into(), latex::small_range(&name)) + } else { + Span::empty(offset) + }; + + let pair = token + .parent_ancestors() + .find_map(latex::KeyValuePair::cast)?; + + if pair.key()?.to_string() == "cite" { + let body = pair.syntax().parent()?; + let group = body.parent()?; + latex::AcronymDeclaration::cast(group.parent()?)?; + Some(span) + } else { + None + } +} |