refactor: split completion functions by topics (#1083)

This commit is contained in:
Myriad-Dreamin 2024-12-28 12:45:45 +08:00 committed by GitHub
parent c5981b81db
commit 81d3ea64c2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 2197 additions and 2163 deletions

View file

@ -0,0 +1,238 @@
use super::*;
impl CompletionPair<'_, '_, '_> {
/// Add completions for all font families.
pub fn font_completions(&mut self) {
let equation = self.cursor.before_window(25).contains("equation");
for (family, iter) in self.worker.world().clone().book().families() {
let detail = summarize_font_family(iter);
if !equation || family.contains("Math") {
self.value_completion(
None,
&Value::Str(family.into()),
false,
Some(detail.as_str()),
);
}
}
}
/// Add completions for all available packages.
pub fn package_completions(&mut self, all_versions: bool) {
let w = self.worker.world().clone();
let mut packages: Vec<_> = w
.packages()
.iter()
.map(|(spec, desc)| (spec, desc.clone()))
.collect();
// local_packages to references and add them to the packages
let local_packages_refs = self.worker.ctx.local_packages();
packages.extend(
local_packages_refs
.iter()
.map(|spec| (spec, Some(eco_format!("{} v{}", spec.name, spec.version)))),
);
packages.sort_by_key(|(spec, _)| (&spec.namespace, &spec.name, Reverse(spec.version)));
if !all_versions {
packages.dedup_by_key(|(spec, _)| (&spec.namespace, &spec.name));
}
for (package, description) in packages {
self.value_completion(
None,
&Value::Str(format_str!("{package}")),
false,
description.as_deref(),
);
}
}
/// Add completions for raw block tags.
pub fn raw_completions(&mut self) {
for (name, mut tags) in RawElem::languages() {
let lower = name.to_lowercase();
if !tags.contains(&lower.as_str()) {
tags.push(lower.as_str());
}
tags.retain(|tag| is_ident(tag));
if tags.is_empty() {
continue;
}
self.push_completion(Completion {
kind: CompletionKind::Constant,
label: name.into(),
apply: Some(tags[0].into()),
detail: Some(repr::separated_list(&tags, " or ").into()),
..Completion::default()
});
}
}
/// Add completions for labels and references.
pub fn ref_completions(&mut self) {
self.label_completions_(false, true);
}
/// Add completions for labels and references.
pub fn label_completions(&mut self, only_citation: bool) {
self.label_completions_(only_citation, false);
}
/// Add completions for labels and references.
pub fn label_completions_(&mut self, only_citation: bool, ref_label: bool) {
let Some(document) = self.worker.document else {
return;
};
let (labels, split) = analyze_labels(document);
let head = &self.cursor.text[..self.cursor.from];
let at = head.ends_with('@');
let open = !at && !head.ends_with('<');
let close = !at && !self.cursor.after.starts_with('>');
let citation = !at && only_citation;
let (skip, take) = if at || ref_label {
(0, usize::MAX)
} else if citation {
(split, usize::MAX)
} else {
(0, split)
};
for DynLabel {
label,
label_desc,
detail,
bib_title,
} in labels.into_iter().skip(skip).take(take)
{
if !self.worker.seen_casts.insert(hash128(&label)) {
continue;
}
let label: EcoString = label.as_str().into();
let completion = Completion {
kind: CompletionKind::Reference,
apply: Some(eco_format!(
"{}{}{}",
if open { "<" } else { "" },
label.as_str(),
if close { ">" } else { "" }
)),
label: label.clone(),
label_details: label_desc.clone(),
filter_text: Some(label.clone()),
detail: detail.clone(),
..Completion::default()
};
if let Some(bib_title) = bib_title {
// Note that this completion re-uses the above `apply` field to
// alter the `bib_title` to the corresponding label.
self.push_completion(Completion {
kind: CompletionKind::Constant,
label: bib_title.clone(),
label_details: Some(label),
filter_text: Some(bib_title),
detail,
..completion.clone()
});
}
self.push_completion(completion);
}
}
/// Add a completion for a specific value.
pub fn value_completion(
&mut self,
label: Option<EcoString>,
value: &Value,
parens: bool,
docs: Option<&str>,
) {
self.value_completion_(
label,
value,
parens,
match value {
Value::Symbol(s) => Some(symbol_label_detail(s.get())),
_ => None,
},
docs,
);
}
/// Add a completion for a specific value.
pub fn value_completion_(
&mut self,
label: Option<EcoString>,
value: &Value,
parens: bool,
label_details: Option<EcoString>,
docs: Option<&str>,
) {
// Prevent duplicate completions from appearing.
if !self.worker.seen_casts.insert(hash128(&(&label, &value))) {
return;
}
let at = label.as_deref().is_some_and(|field| !is_ident(field));
let label = label.unwrap_or_else(|| value.repr());
let detail = docs.map(Into::into).or_else(|| match value {
Value::Symbol(symbol) => Some(symbol_detail(symbol.get())),
Value::Func(func) => func.docs().map(plain_docs_sentence),
Value::Type(ty) => Some(plain_docs_sentence(ty.docs())),
v => {
let repr = v.repr();
(repr.as_str() != label).then_some(repr)
}
});
let mut apply = None;
let mut command = None;
if parens && matches!(value, Value::Func(_)) {
if let Value::Func(func) = value {
command = self.worker.ctx.analysis.trigger_parameter_hints(true);
if func
.params()
.is_some_and(|params| params.iter().all(|param| param.name == "self"))
{
apply = Some(eco_format!("{label}()${{}}"));
} else {
apply = Some(eco_format!("{label}(${{}})"));
}
}
} else if at {
apply = Some(eco_format!("at(\"{label}\")"));
} else {
let apply_label = &mut label.as_str();
if apply_label.ends_with('"') && self.cursor.after.starts_with('"') {
if let Some(trimmed) = apply_label.strip_suffix('"') {
*apply_label = trimmed;
}
}
let from_before = slice_at(self.cursor.text, 0..self.cursor.from);
if apply_label.starts_with('"') && from_before.ends_with('"') {
if let Some(trimmed) = apply_label.strip_prefix('"') {
*apply_label = trimmed;
}
}
if apply_label.len() != label.len() {
apply = Some((*apply_label).into());
}
}
self.push_completion(Completion {
kind: value_to_completion_kind(value),
label,
apply,
detail,
label_details,
command: command.map(From::from),
..Completion::default()
});
}
}