diff options
Diffstat (limited to 'support/texlab/src/features/completion/theorem.rs')
-rw-r--r-- | support/texlab/src/features/completion/theorem.rs | 152 |
1 files changed, 152 insertions, 0 deletions
diff --git a/support/texlab/src/features/completion/theorem.rs b/support/texlab/src/features/completion/theorem.rs new file mode 100644 index 0000000000..54db031a4b --- /dev/null +++ b/support/texlab/src/features/completion/theorem.rs @@ -0,0 +1,152 @@ +use cancellation::CancellationToken; +use cstree::TextRange; +use lsp_types::CompletionParams; + +use crate::{ + features::cursor::CursorContext, + syntax::{latex, CstNode}, +}; + +use super::types::{InternalCompletionItem, InternalCompletionItemData}; + +pub fn complete_theorem_environments<'a>( + context: &'a CursorContext<CompletionParams>, + items: &mut Vec<InternalCompletionItem<'a>>, + token: &CancellationToken, +) -> Option<()> { + if token.is_canceled() { + return None; + } + + let token = context.cursor.as_latex()?; + let group = latex::CurlyGroupWord::cast(token.parent()) + .filter(|group| context.is_inside_latex_curly(group))?; + if !matches!(group.syntax().parent()?.kind(), latex::BEGIN | latex::END) { + return None; + } + + let range = if token.kind() == latex::WORD { + token.text_range() + } else { + TextRange::empty(context.offset) + }; + + for document in &context.request.subset.documents { + if let Some(data) = document.data.as_latex() { + for environment in &data.extras.theorem_environments { + items.push(InternalCompletionItem::new( + range, + InternalCompletionItemData::UserEnvironment { + name: &environment.name, + }, + )); + } + } + } + + Some(()) +} + +#[cfg(test)] +mod tests { + use crate::features::testing::FeatureTester; + + use super::*; + + #[test] + fn test_empty_latex_document() { + let request = FeatureTester::builder() + .files(vec![("main.tex", "")]) + .main("main.tex") + .line(0) + .character(0) + .build() + .completion(); + + let context = CursorContext::new(request); + let mut actual_items = Vec::new(); + complete_theorem_environments(&context, &mut actual_items, CancellationToken::none()); + + assert!(actual_items.is_empty()); + } + + #[test] + fn test_empty_bibtex_document() { + let request = FeatureTester::builder() + .files(vec![("main.bib", "")]) + .main("main.bib") + .line(0) + .character(0) + .build() + .completion(); + + let context = CursorContext::new(request); + let mut actual_items = Vec::new(); + complete_theorem_environments(&context, &mut actual_items, CancellationToken::none()); + + assert!(actual_items.is_empty()); + } + + #[test] + fn test_simple() { + let request = FeatureTester::builder() + .files(vec![("main.tex", "\\begin{ \\newtheorem{lemma}{Lemma}")]) + .main("main.tex") + .line(0) + .character(7) + .build() + .completion(); + + let context = CursorContext::new(request); + let mut actual_items = Vec::new(); + complete_theorem_environments(&context, &mut actual_items, CancellationToken::none()); + + assert!(!actual_items.is_empty()); + for item in actual_items { + assert_eq!(item.range, TextRange::new(7.into(), 7.into())); + } + } + + #[test] + fn test_simple_end() { + let request = FeatureTester::builder() + .files(vec![( + "main.tex", + "\\newtheorem{lemma}{Lemma}\\begin{a}\n\\end{", + )]) + .main("main.tex") + .line(1) + .character(5) + .build() + .completion(); + + let context = CursorContext::new(request); + let mut actual_items = Vec::new(); + complete_theorem_environments(&context, &mut actual_items, CancellationToken::none()); + + assert!(!actual_items.is_empty()); + for item in actual_items { + assert_eq!(item.range, TextRange::new(40.into(), 40.into())); + } + } + + #[test] + fn test_simple_existing() { + let request = FeatureTester::builder() + .files(vec![("main.tex", "\\begin{d}\\newtheorem{lemma}{Lemma}")]) + .main("main.tex") + .line(0) + .character(7) + .build() + .completion(); + + let context = CursorContext::new(request); + let mut actual_items = Vec::new(); + complete_theorem_environments(&context, &mut actual_items, CancellationToken::none()); + + assert!(!actual_items.is_empty()); + for item in actual_items { + assert_eq!(item.range, TextRange::new(7.into(), 8.into())); + } + } +} |