mirror of
https://github.com/typst/typst
synced 2025-05-14 04:56:26 +08:00
Better font autocompletion
This commit is contained in:
parent
09077d6382
commit
87991b81d6
@ -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
|
||||||
|
@ -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");
|
||||||
}
|
}
|
||||||
|
@ -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, ¶m);
|
named_param_value_completions(ctx, &callee, ¶m);
|
||||||
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(¶m.cast);
|
ctx.cast_completions(¶m.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 {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user