Fix autocomplete

This commit is contained in:
Laurenz 2022-12-25 17:20:12 +01:00
parent fdf5f864fa
commit 046cd4f5c7
2 changed files with 63 additions and 29 deletions

View File

@ -3,7 +3,7 @@ use crate::prelude::*;
use crate::text::TextNode; use crate::text::TextNode;
/// # Bullet List /// # Bullet List
/// An bullet list. /// A bullet list.
/// ///
/// Displays a sequence of items vertically, with each item introduced by a /// Displays a sequence of items vertically, with each item introduced by a
/// marker. /// marker.

View File

@ -307,8 +307,7 @@ fn complete_math(ctx: &mut CompletionContext) -> bool {
// Behind existing atom or identifier: "$a|$" or "$abc|$". // Behind existing atom or identifier: "$a|$" or "$abc|$".
if matches!(ctx.leaf.kind(), SyntaxKind::Atom(_) | SyntaxKind::Ident(_)) { if matches!(ctx.leaf.kind(), SyntaxKind::Atom(_) | SyntaxKind::Ident(_)) {
ctx.from = ctx.leaf.offset(); ctx.from = ctx.leaf.offset();
ctx.symbol_completions(false); ctx.math_completions();
ctx.scope_completions();
return true; return true;
} }
@ -334,7 +333,7 @@ fn complete_code(ctx: &mut CompletionContext) -> bool {
// An existing identifier: "{ pa| }". // An existing identifier: "{ pa| }".
if matches!(ctx.leaf.kind(), SyntaxKind::Ident(_)) { if matches!(ctx.leaf.kind(), SyntaxKind::Ident(_)) {
ctx.from = ctx.leaf.offset(); ctx.from = ctx.leaf.offset();
ctx.expr_completions(true); ctx.expr_completions(false);
return true; return true;
} }
@ -413,13 +412,8 @@ impl<'a> CompletionContext<'a> {
}); });
} }
/// Add completions for the global scope.
fn scope_completions(&mut self) {
self.scope_completions_where(|_| true);
}
/// Add completions for a subset of the global scope. /// Add completions for a subset of the global scope.
fn scope_completions_where(&mut self, filter: impl Fn(&Value) -> bool) { fn scope_completions(&mut self, filter: impl Fn(&Value) -> bool) {
for (name, value) in self.scope.iter() { for (name, value) in self.scope.iter() {
if filter(value) { if filter(value) {
self.value_completion(Some(name.clone()), value, None); self.value_completion(Some(name.clone()), value, None);
@ -459,7 +453,7 @@ impl<'a> CompletionContext<'a> {
kind: CompletionKind::Param, kind: CompletionKind::Param,
label: param.name.into(), label: param.name.into(),
apply: Some(format_eco!("{}: ${{}}", param.name)), apply: Some(format_eco!("{}: ${{}}", param.name)),
detail: Some(param.docs.into()), detail: Some(plain_docs_sentence(param.docs)),
}); });
} }
@ -529,7 +523,7 @@ impl<'a> CompletionContext<'a> {
"cmyk(${c}, ${m}, ${y}, ${k})", "cmyk(${c}, ${m}, ${y}, ${k})",
"A custom CMYK color.", "A custom CMYK color.",
); );
self.scope_completions_where(|value| value.type_name() == "color"); self.scope_completions(|value| value.type_name() == "color");
} }
CastInfo::Type("function") => { CastInfo::Type("function") => {
self.snippet_completion( self.snippet_completion(
@ -545,7 +539,7 @@ impl<'a> CompletionContext<'a> {
apply: Some(format_eco!("${{{ty}}}")), apply: Some(format_eco!("${{{ty}}}")),
detail: Some(format_eco!("A value of type {ty}.")), detail: Some(format_eco!("A value of type {ty}.")),
}); });
self.scope_completions_where(|value| value.type_name() == *ty); self.scope_completions(|value| value.type_name() == *ty);
} }
CastInfo::Union(union) => { CastInfo::Union(union) => {
for info in union { for info in union {
@ -565,19 +559,14 @@ impl<'a> CompletionContext<'a> {
let mut label = label.unwrap_or_else(|| value.repr().into()); let mut label = label.unwrap_or_else(|| value.repr().into());
let mut apply = None; let mut apply = None;
if matches!(value, Value::Func(_)) { if label.starts_with('"') {
apply = Some(format_eco!("{label}(${{}})")); let trimmed = label.trim_matches('"').into();
label.push_str("()"); apply = Some(label);
} else { label = trimmed;
if label.starts_with('"') {
let trimmed = label.trim_matches('"').into();
apply = Some(label);
label = trimmed;
}
} }
let detail = docs.map(Into::into).or_else(|| match value { let detail = docs.map(Into::into).or_else(|| match value {
Value::Func(func) => func.info().map(|info| info.docs.into()), Value::Func(func) => func.info().map(|info| plain_docs_sentence(info.docs)),
Value::Color(color) => Some(format_eco!("The color {color:?}.")), Value::Color(color) => Some(format_eco!("The color {color:?}.")),
Value::Auto => Some("A smart default.".into()), Value::Auto => Some("A smart default.".into()),
_ => None, _ => None,
@ -770,7 +759,7 @@ impl<'a> CompletionContext<'a> {
_ => true, _ => true,
}); });
self.scope_completions_where(|value| { self.scope_completions(|value| {
matches!( matches!(
value, value,
Value::Func(func) if func.info().map_or(false, |info| { Value::Func(func) if func.info().map_or(false, |info| {
@ -801,7 +790,7 @@ impl<'a> CompletionContext<'a> {
/// Add completions for expression snippets. /// Add completions for expression snippets.
#[rustfmt::skip] #[rustfmt::skip]
fn expr_completions(&mut self, short_form: bool) { fn expr_completions(&mut self, short_form: bool) {
self.scope_completions_where(|value| { self.scope_completions(|value| {
!short_form || matches!( !short_form || matches!(
value, value,
Value::Func(func) if func.info().map_or(true, |info| { Value::Func(func) if func.info().map_or(true, |info| {
@ -943,7 +932,7 @@ impl<'a> CompletionContext<'a> {
/// Add completions for all functions from the global scope. /// Add completions for all functions from the global scope.
fn set_rule_completions(&mut self) { fn set_rule_completions(&mut self) {
self.scope_completions_where(|value| { self.scope_completions(|value| {
matches!( matches!(
value, value,
Value::Func(func) if func.info().map_or(false, |info| { Value::Func(func) if func.info().map_or(false, |info| {
@ -955,7 +944,7 @@ impl<'a> CompletionContext<'a> {
/// Add completions for selectors. /// Add completions for selectors.
fn show_rule_selector_completions(&mut self) { fn show_rule_selector_completions(&mut self) {
self.scope_completions_where( self.scope_completions(
|value| matches!(value, Value::Func(func) if func.select(None).is_ok()), |value| matches!(value, Value::Func(func) if func.select(None).is_ok()),
); );
@ -974,7 +963,7 @@ impl<'a> CompletionContext<'a> {
); );
} }
/// Add completions for selectors. /// Add completions for recipes.
fn show_rule_recipe_completions(&mut self) { fn show_rule_recipe_completions(&mut self) {
self.snippet_completion( self.snippet_completion(
"replacement", "replacement",
@ -994,6 +983,51 @@ impl<'a> CompletionContext<'a> {
"Transform the element with a function.", "Transform the element with a function.",
); );
self.scope_completions_where(|value| matches!(value, Value::Func(_))); self.scope_completions(|value| matches!(value, Value::Func(_)));
} }
} }
/// Extract the first sentence of plain text of a piece of documentation.
///
/// Removes Markdown formatting.
fn plain_docs_sentence(docs: &str) -> EcoString {
let mut s = unscanny::Scanner::new(docs);
let mut output = String::new();
let mut link = false;
while let Some(c) = s.eat() {
match c {
'`' => {
let mut raw = s.eat_until('`');
if (raw.starts_with('{') && raw.ends_with('}'))
|| (raw.starts_with('[') && raw.ends_with(']'))
{
raw = &raw[1..raw.len() - 1];
}
s.eat();
output.push('`');
output.push_str(raw);
output.push('`');
}
'[' => link = true,
']' if link => {
if s.eat_if('(') {
s.eat_until(')');
s.eat();
} else if s.eat_if('[') {
s.eat_until(']');
s.eat();
}
link = false
}
'*' | '_' => {}
'.' => {
output.push('.');
break;
}
_ => output.push(c),
}
}
output.into()
}