use base_db::semantics::Span; use rowan::ast::AstNode; use syntax::latex; use crate::{ util::{find_curly_group_word, included_packages, CompletionBuilder, ProviderContext}, CompletionItem, CompletionItemData, CompletionParams, EnvironmentData, }; pub fn complete_environments<'a>( params: &'a CompletionParams<'a>, builder: &mut CompletionBuilder<'a>, ) -> Option<()> { let (cursor, group) = find_environment(params)?; let begin = group .syntax() .parent() .and_then(|node| node.parent()) .and_then(latex::Environment::cast) .and_then(|env| env.begin()) .and_then(|begin| begin.name()) .and_then(|name| name.key()) .map_or_else(String::new, |name| name.to_string()); let mut proc = Processor { inner: ProviderContext { builder, params, cursor, }, begin, }; proc.add_library(); proc.add_theorem(); proc.add_user(); Some(()) } struct Processor<'a, 'b> { inner: ProviderContext<'a, 'b>, begin: String, } impl<'a, 'b> Processor<'a, 'b> { fn add_library(&mut self) { for package in included_packages(&self.inner.params.feature) { let envs_with_score = package.environments.iter().filter_map(|env| { let matcher = &self.inner.builder.matcher; let score = matcher.score(env, &self.inner.cursor.text)?; Some((*env, score)) }); for (name, score) in envs_with_score { let data = CompletionItemData::Environment(EnvironmentData { name, package: Some(package), }); self.inner.builder.items.push(CompletionItem { score, data, range: self.inner.cursor.range, preselect: name == self.begin, }); } } } fn add_theorem(&mut self) { let documents = self.inner.params.feature.project.documents.iter(); for theorem in documents .filter_map(|document| document.data.as_tex()) .flat_map(|data| data.semantics.theorem_definitions.iter()) { let matcher = &self.inner.builder.matcher; let name = theorem.name.text.as_str(); if let Some(score) = matcher.score(name, &self.inner.cursor.text) { let data = CompletionItemData::Environment(EnvironmentData { name, package: None, }); self.inner.builder.items.push(CompletionItem { score, data, range: self.inner.cursor.range, preselect: name == self.begin, }); } } } fn add_user(&mut self) { let documents = self.inner.params.feature.project.documents.iter(); for data in documents.filter_map(|document| document.data.as_tex()) { let envs = data .semantics .environments .iter() .filter(|name| name.range != self.inner.cursor.range); let envs_with_score = envs.filter_map(|env| { let matcher = &self.inner.builder.matcher; let score = matcher.score(&env.text, &self.inner.cursor.text)?; Some((&env.text, score)) }); for (name, score) in envs_with_score { let data = CompletionItemData::Environment(EnvironmentData { name, package: None, }); self.inner.builder.items.push(CompletionItem { score, data, range: self.inner.cursor.range, preselect: name == &self.begin, }); } } } } fn find_environment(params: &CompletionParams) -> Option<(Span, latex::CurlyGroupWord)> { let (span, group) = find_curly_group_word(params)?; let parent = group.syntax().parent()?; if matches!(parent.kind(), latex::BEGIN | latex::END) { Some((span, group)) } else { None } }