diff --git a/library/src/text/mod.rs b/library/src/text/mod.rs index 2c7cac04d..292179449 100644 --- a/library/src/text/mod.rs +++ b/library/src/text/mod.rs @@ -40,7 +40,7 @@ use crate::prelude::*; /// ``` /// /// ## Parameters -/// - family: `EcoString` (positional, variadic, settable) +/// - family: `FallbackList` (positional, named, variadic, settable) /// A prioritized sequence of font families. /// /// When processing text, Typst tries all specified font families in order diff --git a/macros/src/func.rs b/macros/src/func.rs index e97684044..41504b208 100644 --- a/macros/src/func.rs +++ b/macros/src/func.rs @@ -148,10 +148,7 @@ fn params(docs: &mut String) -> Result<(Vec, Vec)> { } } - if (!named && !positional) - || (variadic && !positional) - || (named && variadic) - || (required && variadic) + if (!named && !positional) || (variadic && !positional) || (required && variadic) { bail!(callsite, "invalid combination of parameter flags"); } diff --git a/src/ide/complete.rs b/src/ide/complete.rs index a24eb7e2e..4271880e8 100644 --- a/src/ide/complete.rs +++ b/src/ide/complete.rs @@ -62,8 +62,6 @@ pub enum CompletionKind { Param, /// A constant. Constant, - /// A font family. - Font, /// A symbol. 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: |)". if_chain! { - if let Some(prev) = ctx.leaf.prev_leaf(); - if let Some(before_colon) = match (prev.kind(), ctx.leaf.kind()) { - (_, SyntaxKind::Colon) => Some(prev), - (SyntaxKind::Colon, _) => prev.prev_leaf(), - _ => None, - }; - if let Some(param) = before_colon.cast::(); + if deciding.kind() == SyntaxKind::Colon; + if let Some(prev) = deciding.prev_leaf(); + if let Some(param) = prev.cast::(); then { - ctx.from = match ctx.leaf.kind() { - SyntaxKind::Colon | SyntaxKind::Space => ctx.cursor, - _ => ctx.leaf.offset(), - }; + if let Some(next) = deciding.next_leaf() { + ctx.from = ctx.cursor.min(next.offset()); + } + named_param_value_completions(ctx, &callee, ¶m); return true; } @@ -560,23 +564,12 @@ fn complete_params(ctx: &mut CompletionContext) -> bool { // Parameters: "func(|)", "func(hi|)", "func(12,|)". if_chain! { - if let Some(deciding) = if ctx.leaf.kind().is_trivia() { - ctx.leaf.prev_leaf() - } else { - Some(ctx.leaf.clone()) - }; - if matches!( - deciding.kind(), - SyntaxKind::LeftParen - | SyntaxKind::Comma - | SyntaxKind::Ident - ); + if matches!(deciding.kind(), SyntaxKind::LeftParen | SyntaxKind::Comma); if deciding.kind() != SyntaxKind::Comma || deciding.range().end < ctx.cursor; then { - ctx.from = match deciding.kind() { - SyntaxKind::Ident => deciding.offset(), - _ => ctx.cursor, - }; + if let Some(next) = deciding.next_leaf() { + ctx.from = ctx.cursor.min(next.offset()); + } // Exclude arguments which are already present. let exclude: Vec<_> = args.items().filter_map(|arg| match arg { @@ -606,10 +599,6 @@ fn param_completions( else { return; } }; - if callee.as_str() == "text" { - ctx.font_completions(); - } - for param in &info.params { if exclude.iter().any(|ident| ident.as_str() == param.name) { continue; @@ -633,6 +622,10 @@ fn param_completions( } } + if callee.as_str() == "text" { + ctx.font_completions(); + } + if ctx.before.ends_with(',') { ctx.enrich(" ", ""); } @@ -655,6 +648,10 @@ fn named_param_value_completions( ctx.cast_completions(¶m.cast); + if callee.as_str() == "text" && name == "family" { + ctx.font_completions(); + } + if ctx.before.ends_with(':') { ctx.enrich(" ", ""); } @@ -836,6 +833,7 @@ struct CompletionContext<'a> { global: &'a Scope, math: &'a Scope, before: &'a str, + after: &'a str, leaf: LinkedNode<'a>, cursor: usize, explicit: bool, @@ -860,6 +858,7 @@ impl<'a> CompletionContext<'a> { global: &world.library().global.scope(), math: &world.library().math.scope(), before: &text[..cursor], + after: &text[cursor..], leaf, cursor, explicit, @@ -896,12 +895,12 @@ impl<'a> CompletionContext<'a> { fn font_completions(&mut self) { for (family, iter) in self.world.book().families() { let detail = summarize_font_family(iter); - self.completions.push(Completion { - kind: CompletionKind::Font, - label: family.into(), - apply: Some(format_eco!("\"{family}\"")), - detail: Some(detail.into()), - }) + self.value_completion( + None, + &Value::Str(family.into()), + false, + Some(detail.as_str()), + ); } } @@ -911,15 +910,15 @@ impl<'a> CompletionContext<'a> { label: Option, value: &Value, 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; - if label.starts_with('"') { - let trimmed = label.trim_matches('"').into(); - apply = Some(label); - label = trimmed; + if label.starts_with('"') && self.after.starts_with('"') { + if let Some(trimmed) = label.strip_suffix('"') { + apply = Some(trimmed.into()); + } } let detail = docs.map(Into::into).or_else(|| match value {