Better font autocompletion

This commit is contained in:
Laurenz 2023-02-24 16:03:58 +01:00
parent 09077d6382
commit 87991b81d6
3 changed files with 45 additions and 49 deletions

View File

@ -40,7 +40,7 @@ use crate::prelude::*;
/// ``` /// ```
/// ///
/// ## Parameters /// ## Parameters
/// - family: `EcoString` (positional, variadic, settable) /// - family: `FallbackList` (positional, named, variadic, settable)
/// A prioritized sequence of font families. /// A prioritized sequence of font families.
/// ///
/// When processing text, Typst tries all specified font families in order /// When processing text, Typst tries all specified font families in order

View File

@ -148,10 +148,7 @@ fn params(docs: &mut String) -> Result<(Vec<TokenStream>, Vec<String>)> {
} }
} }
if (!named && !positional) if (!named && !positional) || (variadic && !positional) || (required && variadic)
|| (variadic && !positional)
|| (named && variadic)
|| (required && variadic)
{ {
bail!(callsite, "invalid combination of parameter flags"); bail!(callsite, "invalid combination of parameter flags");
} }

View File

@ -62,8 +62,6 @@ pub enum CompletionKind {
Param, Param,
/// A constant. /// A constant.
Constant, Constant,
/// A font family.
Font,
/// A symbol. /// A symbol.
Symbol(char), Symbol(char),
} }
@ -539,20 +537,26 @@ fn complete_params(ctx: &mut CompletionContext) -> bool {
} }
}; };
// Find the piece of syntax that decides what we're completion.
let mut deciding = ctx.leaf.clone();
while !matches!(
deciding.kind(),
SyntaxKind::LeftParen | SyntaxKind::Comma | SyntaxKind::Colon
) {
let Some(prev) = deciding.prev_leaf() else { break };
deciding = prev;
}
// Parameter values: "func(param:|)", "func(param: |)". // Parameter values: "func(param:|)", "func(param: |)".
if_chain! { if_chain! {
if let Some(prev) = ctx.leaf.prev_leaf(); if deciding.kind() == SyntaxKind::Colon;
if let Some(before_colon) = match (prev.kind(), ctx.leaf.kind()) { if let Some(prev) = deciding.prev_leaf();
(_, SyntaxKind::Colon) => Some(prev), if let Some(param) = prev.cast::<ast::Ident>();
(SyntaxKind::Colon, _) => prev.prev_leaf(),
_ => None,
};
if let Some(param) = before_colon.cast::<ast::Ident>();
then { then {
ctx.from = match ctx.leaf.kind() { if let Some(next) = deciding.next_leaf() {
SyntaxKind::Colon | SyntaxKind::Space => ctx.cursor, ctx.from = ctx.cursor.min(next.offset());
_ => ctx.leaf.offset(), }
};
named_param_value_completions(ctx, &callee, &param); named_param_value_completions(ctx, &callee, &param);
return true; return true;
} }
@ -560,23 +564,12 @@ fn complete_params(ctx: &mut CompletionContext) -> bool {
// Parameters: "func(|)", "func(hi|)", "func(12,|)". // Parameters: "func(|)", "func(hi|)", "func(12,|)".
if_chain! { if_chain! {
if let Some(deciding) = if ctx.leaf.kind().is_trivia() { if matches!(deciding.kind(), SyntaxKind::LeftParen | SyntaxKind::Comma);
ctx.leaf.prev_leaf()
} else {
Some(ctx.leaf.clone())
};
if matches!(
deciding.kind(),
SyntaxKind::LeftParen
| SyntaxKind::Comma
| SyntaxKind::Ident
);
if deciding.kind() != SyntaxKind::Comma || deciding.range().end < ctx.cursor; if deciding.kind() != SyntaxKind::Comma || deciding.range().end < ctx.cursor;
then { then {
ctx.from = match deciding.kind() { if let Some(next) = deciding.next_leaf() {
SyntaxKind::Ident => deciding.offset(), ctx.from = ctx.cursor.min(next.offset());
_ => ctx.cursor, }
};
// Exclude arguments which are already present. // Exclude arguments which are already present.
let exclude: Vec<_> = args.items().filter_map(|arg| match arg { let exclude: Vec<_> = args.items().filter_map(|arg| match arg {
@ -606,10 +599,6 @@ fn param_completions(
else { return; } else { return; }
}; };
if callee.as_str() == "text" {
ctx.font_completions();
}
for param in &info.params { for param in &info.params {
if exclude.iter().any(|ident| ident.as_str() == param.name) { if exclude.iter().any(|ident| ident.as_str() == param.name) {
continue; continue;
@ -633,6 +622,10 @@ fn param_completions(
} }
} }
if callee.as_str() == "text" {
ctx.font_completions();
}
if ctx.before.ends_with(',') { if ctx.before.ends_with(',') {
ctx.enrich(" ", ""); ctx.enrich(" ", "");
} }
@ -655,6 +648,10 @@ fn named_param_value_completions(
ctx.cast_completions(&param.cast); ctx.cast_completions(&param.cast);
if callee.as_str() == "text" && name == "family" {
ctx.font_completions();
}
if ctx.before.ends_with(':') { if ctx.before.ends_with(':') {
ctx.enrich(" ", ""); ctx.enrich(" ", "");
} }
@ -836,6 +833,7 @@ struct CompletionContext<'a> {
global: &'a Scope, global: &'a Scope,
math: &'a Scope, math: &'a Scope,
before: &'a str, before: &'a str,
after: &'a str,
leaf: LinkedNode<'a>, leaf: LinkedNode<'a>,
cursor: usize, cursor: usize,
explicit: bool, explicit: bool,
@ -860,6 +858,7 @@ impl<'a> CompletionContext<'a> {
global: &world.library().global.scope(), global: &world.library().global.scope(),
math: &world.library().math.scope(), math: &world.library().math.scope(),
before: &text[..cursor], before: &text[..cursor],
after: &text[cursor..],
leaf, leaf,
cursor, cursor,
explicit, explicit,
@ -896,12 +895,12 @@ impl<'a> CompletionContext<'a> {
fn font_completions(&mut self) { fn font_completions(&mut self) {
for (family, iter) in self.world.book().families() { for (family, iter) in self.world.book().families() {
let detail = summarize_font_family(iter); let detail = summarize_font_family(iter);
self.completions.push(Completion { self.value_completion(
kind: CompletionKind::Font, None,
label: family.into(), &Value::Str(family.into()),
apply: Some(format_eco!("\"{family}\"")), false,
detail: Some(detail.into()), Some(detail.as_str()),
}) );
} }
} }
@ -911,15 +910,15 @@ impl<'a> CompletionContext<'a> {
label: Option<EcoString>, label: Option<EcoString>,
value: &Value, value: &Value,
parens: bool, parens: bool,
docs: Option<&'static str>, docs: Option<&str>,
) { ) {
let mut label = label.unwrap_or_else(|| value.repr().into()); let label = label.unwrap_or_else(|| value.repr().into());
let mut apply = None; let mut apply = None;
if label.starts_with('"') { if label.starts_with('"') && self.after.starts_with('"') {
let trimmed = label.trim_matches('"').into(); if let Some(trimmed) = label.strip_suffix('"') {
apply = Some(label); apply = Some(trimmed.into());
label = trimmed; }
} }
let detail = docs.map(Into::into).or_else(|| match value { let detail = docs.map(Into::into).or_else(|| match value {