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

View File

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

View File

@ -20,7 +20,6 @@ typst-timing = { workspace = true }
typst-utils = { workspace = true } typst-utils = { workspace = true }
comemo = { workspace = true } comemo = { workspace = true }
ecow = { workspace = true } ecow = { workspace = true }
if_chain = { workspace = true }
indexmap = { workspace = true } indexmap = { workspace = true }
toml = { workspace = true } toml = { workspace = true }
unicode-segmentation = { 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. // Check whether this is a get rule field access.
if_chain::if_chain! { if let Value::Func(func) = &value &&
if let Value::Func(func) = &value; let Some(element) = func.element() &&
if let Some(element) = func.element(); let Some(id) = element.field_id(&field) &&
if let Some(id) = element.field_id(&field); let styles = vm.context.styles().at(field.span()) &&
let styles = vm.context.styles().at(field.span()); let Ok(value) = element.field_from_styles(
if let Ok(value) = element.field_from_styles(
id, id,
styles.as_ref().map(|&s| s).unwrap_or_default(), styles.as_ref().map(|&s| s).unwrap_or_default(),
); ) {
then {
// Only validate the context once we know that this is indeed // Only validate the context once we know that this is indeed
// a field from the style chain. // a field from the style chain.
let _ = styles?; let _ = styles?;
return Ok(value); return Ok(value);
} }
}
Err(err) Err(err)
} }

View File

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

View File

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

View File

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

View File

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