Yeet if_chain macro

This commit is contained in:
Laurenz 2025-07-21 15:11:36 +02:00
parent c9c2315ad3
commit e81a5a6ef2
8 changed files with 118 additions and 171 deletions

8
Cargo.lock generated
View File

@ -1198,12 +1198,6 @@ dependencies = [
"icu_properties",
]
[[package]]
name = "if_chain"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb56e1aa765b4b4f3aadfab769793b7087bb03a4ea4920644a6d238e2df5b9ed"
[[package]]
name = "image"
version = "0.25.5"
@ -2943,7 +2937,6 @@ version = "0.13.1"
dependencies = [
"comemo",
"ecow",
"if_chain",
"indexmap 2.7.1",
"stacker",
"toml",
@ -2991,7 +2984,6 @@ version = "0.13.1"
dependencies = [
"comemo",
"ecow",
"if_chain",
"once_cell",
"pathdiff",
"serde",

View File

@ -68,7 +68,6 @@ icu_provider = { version = "1.4", features = ["sync"] }
icu_provider_adapters = "1.4"
icu_provider_blob = "1.4"
icu_segmenter = { version = "1.4", features = ["serde"] }
if_chain = "1"
image = { version = "0.25.5", default-features = false, features = ["png", "jpeg", "gif", "webp"] }
indexmap = { version = "2", features = ["serde"] }
infer = { version = "0.19.0", default-features = false }

View File

@ -20,7 +20,6 @@ typst-timing = { workspace = true }
typst-utils = { workspace = true }
comemo = { workspace = true }
ecow = { workspace = true }
if_chain = { workspace = true }
indexmap = { workspace = true }
toml = { workspace = true }
unicode-segmentation = { workspace = true }

View File

@ -324,22 +324,19 @@ impl Eval for ast::FieldAccess<'_> {
};
// Check whether this is a get rule field access.
if_chain::if_chain! {
if let Value::Func(func) = &value;
if let Some(element) = func.element();
if let Some(id) = element.field_id(&field);
let styles = vm.context.styles().at(field.span());
if let Ok(value) = element.field_from_styles(
if let Value::Func(func) = &value &&
let Some(element) = func.element() &&
let Some(id) = element.field_id(&field) &&
let styles = vm.context.styles().at(field.span()) &&
let Ok(value) = element.field_from_styles(
id,
styles.as_ref().map(|&s| s).unwrap_or_default(),
);
then {
) {
// Only validate the context once we know that this is indeed
// a field from the style chain.
let _ = styles?;
return Ok(value);
}
}
Err(err)
}

View File

@ -57,12 +57,11 @@ impl Eval for ast::ShowRule<'_> {
/// Migration hint for `show par: set block(spacing: ..)`.
fn check_show_par_set_block(vm: &mut Vm, recipe: &Recipe) {
if_chain::if_chain! {
if let Some(Selector::Elem(elem, _)) = recipe.selector();
if *elem == Element::of::<ParElem>();
if let Transformation::Style(styles) = recipe.transform();
if styles.has(BlockElem::above) || styles.has(BlockElem::below);
then {
if let Some(Selector::Elem(elem, _)) = recipe.selector() &&
*elem == Element::of::<ParElem>() &&
let Transformation::Style(styles) = recipe.transform() &&
(styles.has(BlockElem::above) || styles.has(BlockElem::below))
{
vm.engine.sink.warn(warning!(
recipe.span(),
"`show par: set block(spacing: ..)` has no effect anymore";
@ -71,4 +70,3 @@ fn check_show_par_set_block(vm: &mut Vm, recipe: &Recipe) {
))
}
}
}

View File

@ -17,7 +17,6 @@ typst = { workspace = true }
typst-eval = { workspace = true }
comemo = { workspace = true }
ecow = { workspace = true }
if_chain = { workspace = true }
pathdiff = { workspace = true }
serde = { workspace = true }
unscanny = { workspace = true }

View File

@ -3,7 +3,6 @@ use std::collections::{BTreeMap, HashSet};
use std::ffi::OsStr;
use ecow::{eco_format, EcoString};
use if_chain::if_chain;
use serde::{Deserialize, Serialize};
use typst::foundations::{
fields_on, repr, AutoValue, CastInfo, Func, Label, NoneValue, ParamInfo, Repr,
@ -145,27 +144,23 @@ fn complete_markup(ctx: &mut CompletionContext) -> bool {
}
// Behind a half-completed binding: "#let x = |".
if_chain! {
if let Some(prev) = ctx.leaf.prev_leaf();
if prev.kind() == SyntaxKind::Eq;
if prev.parent_kind() == Some(SyntaxKind::LetBinding);
then {
if let Some(prev) = ctx.leaf.prev_leaf()
&& prev.kind() == SyntaxKind::Eq
&& prev.parent_kind() == Some(SyntaxKind::LetBinding)
{
ctx.from = ctx.cursor;
code_completions(ctx, false);
return true;
}
}
// Behind a half-completed context block: "#context |".
if_chain! {
if let Some(prev) = ctx.leaf.prev_leaf();
if prev.kind() == SyntaxKind::Context;
then {
if let Some(prev) = ctx.leaf.prev_leaf()
&& prev.kind() == SyntaxKind::Context
{
ctx.from = ctx.cursor;
code_completions(ctx, false);
return true;
}
}
// Directly after a raw block.
let mut s = Scanner::new(ctx.text);
@ -373,38 +368,34 @@ fn complete_field_accesses(ctx: &mut CompletionContext) -> bool {
);
// Behind an expression plus dot: "emoji.|".
if_chain! {
if ctx.leaf.kind() == SyntaxKind::Dot
if (ctx.leaf.kind() == SyntaxKind::Dot
|| (matches!(ctx.leaf.kind(), SyntaxKind::Text | SyntaxKind::MathText)
&& ctx.leaf.text() == ".");
if ctx.leaf.range().end == ctx.cursor;
if let Some(prev) = ctx.leaf.prev_sibling();
if !in_markup || prev.range().end == ctx.leaf.range().start;
if prev.is::<ast::Expr>();
if prev.parent_kind() != Some(SyntaxKind::Markup) ||
prev.prev_sibling_kind() == Some(SyntaxKind::Hash);
if let Some((value, styles)) = analyze_expr(ctx.world, &prev).into_iter().next();
then {
&& ctx.leaf.text() == "."))
&& ctx.leaf.range().end == ctx.cursor
&& let Some(prev) = ctx.leaf.prev_sibling()
&& (!in_markup || prev.range().end == ctx.leaf.range().start)
&& prev.is::<ast::Expr>()
&& (prev.parent_kind() != Some(SyntaxKind::Markup) ||
prev.prev_sibling_kind() == Some(SyntaxKind::Hash))
&& let Some((value, styles)) = analyze_expr(ctx.world, &prev).into_iter().next()
{
ctx.from = ctx.cursor;
field_access_completions(ctx, &value, &styles);
return true;
}
}
// Behind a started field access: "emoji.fa|".
if_chain! {
if ctx.leaf.kind() == SyntaxKind::Ident;
if let Some(prev) = ctx.leaf.prev_sibling();
if prev.kind() == SyntaxKind::Dot;
if let Some(prev_prev) = prev.prev_sibling();
if prev_prev.is::<ast::Expr>();
if let Some((value, styles)) = analyze_expr(ctx.world, &prev_prev).into_iter().next();
then {
if ctx.leaf.kind() == SyntaxKind::Ident
&& let Some(prev) = ctx.leaf.prev_sibling()
&& prev.kind() == SyntaxKind::Dot
&& let Some(prev_prev) = prev.prev_sibling()
&& prev_prev.is::<ast::Expr>()
&& let Some((value, styles)) = analyze_expr(ctx.world, &prev_prev).into_iter().next()
{
ctx.from = ctx.leaf.offset();
field_access_completions(ctx, &value, &styles);
return true;
}
}
false
}
@ -507,14 +498,10 @@ fn complete_open_labels(ctx: &mut CompletionContext) -> bool {
fn complete_imports(ctx: &mut CompletionContext) -> bool {
// In an import path for a file or package:
// "#import "|",
if_chain! {
if matches!(
ctx.leaf.parent_kind(),
Some(SyntaxKind::ModuleImport | SyntaxKind::ModuleInclude)
);
if let Some(ast::Expr::Str(str)) = ctx.leaf.cast();
if let Some(SyntaxKind::ModuleImport | SyntaxKind::ModuleInclude) = ctx.leaf.parent_kind()
&& let Some(ast::Expr::Str(str)) = ctx.leaf.cast()
{
let value = str.get();
then {
ctx.from = ctx.leaf.offset();
if value.starts_with('@') {
let all_versions = value.contains(':');
@ -524,41 +511,36 @@ fn complete_imports(ctx: &mut CompletionContext) -> bool {
}
return true;
}
}
// Behind an import list:
// "#import "path.typ": |",
// "#import "path.typ": a, b, |".
if_chain! {
if let Some(prev) = ctx.leaf.prev_sibling();
if let Some(ast::Expr::ModuleImport(import)) = prev.get().cast();
if let Some(ast::Imports::Items(items)) = import.imports();
if let Some(source) = prev.children().find(|child| child.is::<ast::Expr>());
then {
if let Some(prev) = ctx.leaf.prev_sibling()
&& let Some(ast::Expr::ModuleImport(import)) = prev.get().cast()
&& let Some(ast::Imports::Items(items)) = import.imports()
&& let Some(source) = prev.children().find(|child| child.is::<ast::Expr>())
{
ctx.from = ctx.cursor;
import_item_completions(ctx, items, &source);
return true;
}
}
// Behind a half-started identifier in an import list:
// "#import "path.typ": thi|",
if_chain! {
if ctx.leaf.kind() == SyntaxKind::Ident;
if let Some(parent) = ctx.leaf.parent();
if parent.kind() == SyntaxKind::ImportItemPath;
if let Some(grand) = parent.parent();
if grand.kind() == SyntaxKind::ImportItems;
if let Some(great) = grand.parent();
if let Some(ast::Expr::ModuleImport(import)) = great.get().cast();
if let Some(ast::Imports::Items(items)) = import.imports();
if let Some(source) = great.children().find(|child| child.is::<ast::Expr>());
then {
if ctx.leaf.kind() == SyntaxKind::Ident
&& let Some(parent) = ctx.leaf.parent()
&& parent.kind() == SyntaxKind::ImportItemPath
&& let Some(grand) = parent.parent()
&& grand.kind() == SyntaxKind::ImportItems
&& let Some(great) = grand.parent()
&& let Some(ast::Expr::ModuleImport(import)) = great.get().cast()
&& let Some(ast::Imports::Items(items)) = import.imports()
&& let Some(source) = great.children().find(|child| child.is::<ast::Expr>())
{
ctx.from = ctx.leaf.offset();
import_item_completions(ctx, items, &source);
return true;
}
}
false
}
@ -607,16 +589,14 @@ fn complete_rules(ctx: &mut CompletionContext) -> bool {
}
// Behind a half-completed show rule: "show strong: |".
if_chain! {
if let Some(prev) = ctx.leaf.prev_leaf();
if matches!(prev.kind(), SyntaxKind::Colon);
if matches!(prev.parent_kind(), Some(SyntaxKind::ShowRule));
then {
if let Some(prev) = ctx.leaf.prev_leaf()
&& matches!(prev.kind(), SyntaxKind::Colon)
&& matches!(prev.parent_kind(), Some(SyntaxKind::ShowRule))
{
ctx.from = ctx.cursor;
show_rule_recipe_completions(ctx);
return true;
}
}
false
}
@ -682,26 +662,25 @@ fn show_rule_recipe_completions(ctx: &mut CompletionContext) {
/// Complete call and set rule parameters.
fn complete_params(ctx: &mut CompletionContext) -> bool {
// Ensure that we are in a function call or set rule's argument list.
let (callee, set, args, args_linked) = if_chain! {
if let Some(parent) = ctx.leaf.parent();
if let Some(parent) = match parent.kind() {
let (callee, set, args, args_linked) =
if let Some(parent) = ctx.leaf.parent()
&& let Some(parent) = match parent.kind() {
SyntaxKind::Named => parent.parent(),
_ => Some(parent),
};
if let Some(args) = parent.get().cast::<ast::Args>();
if let Some(grand) = parent.parent();
if let Some(expr) = grand.get().cast::<ast::Expr>();
let set = matches!(expr, ast::Expr::SetRule(_));
if let Some(callee) = match expr {
}
&& let Some(args) = parent.get().cast::<ast::Args>()
&& let Some(grand) = parent.parent()
&& let Some(expr) = grand.get().cast::<ast::Expr>()
&& let set = matches!(expr, ast::Expr::SetRule(_))
&& let Some(callee) = match expr {
ast::Expr::FuncCall(call) => Some(call.callee()),
ast::Expr::SetRule(set) => Some(set.target()),
_ => None,
};
then {
}
{
(callee, set, args, parent)
} else {
return false;
}
};
// Find the piece of syntax that decides what we're completing.
@ -718,11 +697,10 @@ fn complete_params(ctx: &mut CompletionContext) -> bool {
}
// Parameter values: "func(param:|)", "func(param: |)".
if_chain! {
if deciding.kind() == SyntaxKind::Colon;
if let Some(prev) = deciding.prev_leaf();
if let Some(param) = prev.get().cast::<ast::Ident>();
then {
if let SyntaxKind::Colon = deciding.kind()
&& let Some(prev) = deciding.prev_leaf()
&& let Some(param) = prev.get().cast::<ast::Ident>()
{
if let Some(next) = deciding.next_leaf() {
ctx.from = ctx.cursor.min(next.offset());
}
@ -730,13 +708,11 @@ fn complete_params(ctx: &mut CompletionContext) -> bool {
named_param_value_completions(ctx, callee, &param);
return true;
}
}
// Parameters: "func(|)", "func(hi|)", "func(12,|)".
if_chain! {
if matches!(deciding.kind(), SyntaxKind::LeftParen | SyntaxKind::Comma);
if deciding.kind() != SyntaxKind::Comma || deciding.range().end < ctx.cursor;
then {
if let SyntaxKind::LeftParen | SyntaxKind::Comma = deciding.kind()
&& (deciding.kind() != SyntaxKind::Comma || deciding.range().end < ctx.cursor)
{
if let Some(next) = deciding.next_leaf() {
ctx.from = ctx.cursor.min(next.offset());
}
@ -744,7 +720,6 @@ fn complete_params(ctx: &mut CompletionContext) -> bool {
param_completions(ctx, callee, set, args, args_linked);
return true;
}
}
false
}
@ -1104,15 +1079,13 @@ fn code_completions(ctx: &mut CompletionContext, hash: bool) {
fn is_in_equation_show_rule(leaf: &LinkedNode<'_>) -> bool {
let mut node = leaf;
while let Some(parent) = node.parent() {
if_chain! {
if let Some(expr) = parent.get().cast::<ast::Expr>();
if let ast::Expr::ShowRule(show) = expr;
if let Some(ast::Expr::FieldAccess(field)) = show.selector();
if field.field().as_str() == "equation";
then {
if let Some(expr) = parent.get().cast::<ast::Expr>()
&& let ast::Expr::ShowRule(show) = expr
&& let Some(ast::Expr::FieldAccess(field)) = show.selector()
&& field.field().as_str() == "equation"
{
return true;
}
}
node = parent;
}
false

View File

@ -1,7 +1,6 @@
use std::fmt::Write;
use ecow::{eco_format, EcoString};
use if_chain::if_chain;
use typst::engine::Sink;
use typst::foundations::{repr, Binding, Capturer, CastInfo, Repr, Value};
use typst::layout::{Length, PagedDocument};
@ -107,20 +106,18 @@ fn expr_tooltip(world: &dyn IdeWorld, leaf: &LinkedNode) -> Option<Tooltip> {
/// Tooltips for imports.
fn import_tooltip(world: &dyn IdeWorld, leaf: &LinkedNode) -> Option<Tooltip> {
if_chain! {
if leaf.kind() == SyntaxKind::Star;
if let Some(parent) = leaf.parent();
if let Some(import) = parent.cast::<ast::ModuleImport>();
if let Some(node) = parent.find(import.source().span());
if let Some(value) = analyze_import(world, &node);
if let Some(scope) = value.scope();
then {
if leaf.kind() == SyntaxKind::Star
&& let Some(parent) = leaf.parent()
&& let Some(import) = parent.cast::<ast::ModuleImport>()
&& let Some(node) = parent.find(import.source().span())
&& let Some(value) = analyze_import(world, &node)
&& let Some(scope) = value.scope()
{
let names: Vec<_> =
scope.iter().map(|(name, ..)| eco_format!("`{name}`")).collect();
let list = repr::separated_list(&names, "and");
return Some(Tooltip::Text(eco_format!("This star imports {list}")));
}
}
None
}
@ -188,51 +185,46 @@ fn label_tooltip(document: &PagedDocument, leaf: &LinkedNode) -> Option<Tooltip>
/// Tooltips for components of a named parameter.
fn named_param_tooltip(world: &dyn IdeWorld, leaf: &LinkedNode) -> Option<Tooltip> {
let (func, named) = if_chain! {
let (func, named) =
// Ensure that we are in a named pair in the arguments to a function
// call or set rule.
if let Some(parent) = leaf.parent();
if let Some(named) = parent.cast::<ast::Named>();
if let Some(grand) = parent.parent();
if matches!(grand.kind(), SyntaxKind::Args);
if let Some(grand_grand) = grand.parent();
if let Some(expr) = grand_grand.cast::<ast::Expr>();
if let Some(ast::Expr::Ident(callee)) = match expr {
if let Some(parent) = leaf.parent()
&& let Some(named) = parent.cast::<ast::Named>()
&& let Some(grand) = parent.parent()
&& matches!(grand.kind(), SyntaxKind::Args)
&& let Some(grand_grand) = grand.parent()
&& let Some(expr) = grand_grand.cast::<ast::Expr>()
&& let Some(ast::Expr::Ident(callee)) = match expr {
ast::Expr::FuncCall(call) => Some(call.callee()),
ast::Expr::SetRule(set) => Some(set.target()),
_ => None,
};
}
// Find metadata about the function.
if let Some(Value::Func(func)) = world
&& let Some(Value::Func(func)) = world
.library()
.global
.scope()
.get(&callee)
.map(Binding::read);
then { (func, named) }
else { return None; }
};
.map(Binding::read)
{ (func, named) }
else { return None; };
// Hovering over the parameter name.
if_chain! {
if leaf.index() == 0;
if let Some(ident) = leaf.cast::<ast::Ident>();
if let Some(param) = func.param(&ident);
then {
if leaf.index() == 0
&& let Some(ident) = leaf.cast::<ast::Ident>()
&& let Some(param) = func.param(&ident)
{
return Some(Tooltip::Text(plain_docs_sentence(param.docs)));
}
}
// Hovering over a string parameter value.
if_chain! {
if let Some(string) = leaf.cast::<ast::Str>();
if let Some(param) = func.param(&named.name());
if let Some(docs) = find_string_doc(&param.input, &string.get());
then {
if let Some(string) = leaf.cast::<ast::Str>()
&& let Some(param) = func.param(&named.name())
&& let Some(docs) = find_string_doc(&param.input, &string.get())
{
return Some(Tooltip::Text(docs.into()));
}
}
None
}
@ -250,27 +242,25 @@ fn find_string_doc(info: &CastInfo, string: &str) -> Option<&'static str> {
/// Tooltip for font.
fn font_tooltip(world: &dyn IdeWorld, leaf: &LinkedNode) -> Option<Tooltip> {
if_chain! {
// Ensure that we are on top of a string.
if let Some(string) = leaf.cast::<ast::Str>();
let lower = string.get().to_lowercase();
if let Some(string) = leaf.cast::<ast::Str>()
&& let lower = string.get().to_lowercase()
// Ensure that we are in the arguments to the text function.
if let Some(parent) = leaf.parent();
if let Some(named) = parent.cast::<ast::Named>();
if named.name().as_str() == "font";
&& let Some(parent) = leaf.parent()
&& let Some(named) = parent.cast::<ast::Named>()
&& named.name().as_str() == "font"
// Find the font family.
if let Some((_, iter)) = world
&& let Some((_, iter)) = world
.book()
.families()
.find(|&(family, _)| family.to_lowercase().as_str() == lower.as_str());
.find(|&(family, _)| family.to_lowercase().as_str() == lower.as_str())
then {
{
let detail = summarize_font_family(iter.collect());
return Some(Tooltip::Text(detail));
}
};
None
}