crates/completion/src/providers/environment.rs
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
}
}