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_user_environments<'a>( context: &'a CursorContext, items: &mut Vec>, cancellation_token: &CancellationToken, ) -> Option<()> { cancellation_token.result().ok()?; 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 name in data .extras .environment_names .iter() .filter(|name| name.as_str() != token.text()) { cancellation_token.result().ok()?; items.push(InternalCompletionItem::new( range, InternalCompletionItemData::UserEnvironment { name }, )); } } } Some(()) } #[cfg(test)] mod tests { use cstree::TextRange; 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_user_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_user_environments(&context, &mut actual_items, CancellationToken::none()); assert!(actual_items.is_empty()); } #[test] fn test_latex_simple() { let request = FeatureTester::builder() .files(vec![( "main.tex", "\\begin{foo}\\end{foo} \\begin{bar}\\end{bar}", )]) .main("main.tex") .line(0) .character(7) .build() .completion(); let context = CursorContext::new(request); let mut actual_items = Vec::new(); complete_user_environments(&context, &mut actual_items, CancellationToken::none()); assert_eq!(actual_items.len(), 1); for item in actual_items { assert_eq!(item.range, TextRange::new(7.into(), 10.into())); } } }