1use std::rc::Rc;
7
8use crate::diagnostics::{BuildDiagnostics, Spanned};
9use crate::expression_tree::{
10 BuiltinFunction, BuiltinMacroFunction, Callable, EasingCurve, Expression, Unit,
11};
12use crate::langtype::{ElementType, Enumeration, EnumerationValue, Type};
13use crate::namedreference::NamedReference;
14use crate::object_tree::{ElementRc, PropertyVisibility};
15use crate::parser::NodeOrToken;
16use crate::typeregister::TypeRegister;
17use smol_str::{SmolStr, ToSmolStr};
18use std::cell::RefCell;
19
20mod named_colors;
21
22pub struct LookupCtx<'a> {
24 pub property_name: Option<&'a str>,
26
27 pub property_type: Type,
30
31 pub component_scope: &'a [ElementRc],
33
34 pub diag: &'a mut BuildDiagnostics,
36
37 pub arguments: Vec<SmolStr>,
39
40 pub type_register: &'a TypeRegister,
42
43 pub type_loader: Option<&'a crate::typeloader::TypeLoader>,
46
47 pub current_token: Option<NodeOrToken>,
49}
50
51impl<'a> LookupCtx<'a> {
52 pub fn empty_context(type_register: &'a TypeRegister, diag: &'a mut BuildDiagnostics) -> Self {
54 Self {
55 property_name: Default::default(),
56 property_type: Default::default(),
57 component_scope: Default::default(),
58 diag,
59 arguments: Default::default(),
60 type_register,
61 type_loader: None,
62 current_token: None,
63 }
64 }
65
66 pub fn return_type(&self) -> &Type {
67 match &self.property_type {
68 Type::Callback(f) | Type::Function(f) => &f.return_type,
69 _ => &self.property_type,
70 }
71 }
72
73 pub fn is_legacy_component(&self) -> bool {
74 self.component_scope.first().is_some_and(|e| e.borrow().is_legacy_syntax)
75 }
76
77 pub fn is_local_element(&self, elem: &ElementRc) -> bool {
79 Option::zip(
80 elem.borrow().enclosing_component.upgrade(),
81 self.component_scope.first().and_then(|x| x.borrow().enclosing_component.upgrade()),
82 )
83 .map_or(true, |(x, y)| Rc::ptr_eq(&x, &y))
84 }
85}
86
87#[derive(Debug)]
88pub enum LookupResult {
89 Expression {
90 expression: Expression,
91 deprecated: Option<String>,
93 },
94 Enumeration(Rc<Enumeration>),
95 Namespace(BuiltinNamespace),
96 Callable(LookupResultCallable),
97}
98
99#[derive(Debug)]
100pub enum LookupResultCallable {
101 Callable(Callable),
102 Macro(BuiltinMacroFunction),
103 MemberFunction {
105 base: Expression,
107 base_node: Option<NodeOrToken>,
108 member: Box<LookupResultCallable>,
109 },
110}
111
112#[derive(Debug, derive_more::Display)]
113pub enum BuiltinNamespace {
114 Colors,
115 Math,
116 Key,
117 SlintInternal,
118}
119
120impl From<Expression> for LookupResult {
121 fn from(expression: Expression) -> Self {
122 Self::Expression { expression, deprecated: None }
123 }
124}
125impl From<Callable> for LookupResult {
126 fn from(callable: Callable) -> Self {
127 Self::Callable(LookupResultCallable::Callable(callable))
128 }
129}
130impl From<BuiltinMacroFunction> for LookupResult {
131 fn from(macro_function: BuiltinMacroFunction) -> Self {
132 Self::Callable(LookupResultCallable::Macro(macro_function))
133 }
134}
135impl From<BuiltinFunction> for LookupResult {
136 fn from(function: BuiltinFunction) -> Self {
137 Self::Callable(LookupResultCallable::Callable(Callable::Builtin(function)))
138 }
139}
140
141impl LookupResult {
142 pub fn deprecated(&self) -> Option<&str> {
143 match self {
144 Self::Expression { deprecated: Some(x), .. } => Some(x.as_str()),
145 _ => None,
146 }
147 }
148}
149
150pub trait LookupObject {
152 fn for_each_entry<R>(
155 &self,
156 ctx: &LookupCtx,
157 f: &mut impl FnMut(&SmolStr, LookupResult) -> Option<R>,
158 ) -> Option<R>;
159
160 fn lookup(&self, ctx: &LookupCtx, name: &SmolStr) -> Option<LookupResult> {
163 self.for_each_entry(ctx, &mut |prop, expr| (prop == name).then_some(expr))
164 }
165}
166
167impl<T1: LookupObject, T2: LookupObject> LookupObject for (T1, T2) {
168 fn for_each_entry<R>(
169 &self,
170 ctx: &LookupCtx,
171 f: &mut impl FnMut(&SmolStr, LookupResult) -> Option<R>,
172 ) -> Option<R> {
173 self.0.for_each_entry(ctx, f).or_else(|| self.1.for_each_entry(ctx, f))
174 }
175
176 fn lookup(&self, ctx: &LookupCtx, name: &SmolStr) -> Option<LookupResult> {
177 self.0.lookup(ctx, name).or_else(|| self.1.lookup(ctx, name))
178 }
179}
180
181impl LookupObject for LookupResult {
182 fn for_each_entry<R>(
183 &self,
184 ctx: &LookupCtx,
185 f: &mut impl FnMut(&SmolStr, LookupResult) -> Option<R>,
186 ) -> Option<R> {
187 match self {
188 LookupResult::Expression { expression, .. } => expression.for_each_entry(ctx, f),
189 LookupResult::Enumeration(e) => e.for_each_entry(ctx, f),
190 LookupResult::Namespace(BuiltinNamespace::Colors) => {
191 (ColorSpecific, ColorFunctions).for_each_entry(ctx, f)
192 }
193 LookupResult::Namespace(BuiltinNamespace::Math) => MathFunctions.for_each_entry(ctx, f),
194 LookupResult::Namespace(BuiltinNamespace::Key) => KeysLookup.for_each_entry(ctx, f),
195 LookupResult::Namespace(BuiltinNamespace::SlintInternal) => {
196 SlintInternal.for_each_entry(ctx, f)
197 }
198 LookupResult::Callable(..) => None,
199 }
200 }
201
202 fn lookup(&self, ctx: &LookupCtx, name: &SmolStr) -> Option<LookupResult> {
203 match self {
204 LookupResult::Expression { expression, .. } => expression.lookup(ctx, name),
205 LookupResult::Enumeration(e) => e.lookup(ctx, name),
206 LookupResult::Namespace(BuiltinNamespace::Colors) => {
207 (ColorSpecific, ColorFunctions).lookup(ctx, name)
208 }
209 LookupResult::Namespace(BuiltinNamespace::Math) => MathFunctions.lookup(ctx, name),
210 LookupResult::Namespace(BuiltinNamespace::Key) => KeysLookup.lookup(ctx, name),
211 LookupResult::Namespace(BuiltinNamespace::SlintInternal) => {
212 SlintInternal.lookup(ctx, name)
213 }
214 LookupResult::Callable(..) => None,
215 }
216 }
217}
218
219struct ArgumentsLookup;
220impl LookupObject for ArgumentsLookup {
221 fn for_each_entry<R>(
222 &self,
223 ctx: &LookupCtx,
224 f: &mut impl FnMut(&SmolStr, LookupResult) -> Option<R>,
225 ) -> Option<R> {
226 let args = match &ctx.property_type {
227 Type::Callback(f) | Type::Function(f) => &f.args,
228 _ => return None,
229 };
230 for (index, (name, ty)) in ctx.arguments.iter().zip(args.iter()).enumerate() {
231 if let Some(r) =
232 f(name, Expression::FunctionParameterReference { index, ty: ty.clone() }.into())
233 {
234 return Some(r);
235 }
236 }
237 None
238 }
239}
240
241struct SpecialIdLookup;
242impl LookupObject for SpecialIdLookup {
243 fn for_each_entry<R>(
244 &self,
245 ctx: &LookupCtx,
246 f: &mut impl FnMut(&SmolStr, LookupResult) -> Option<R>,
247 ) -> Option<R> {
248 let last = ctx.component_scope.last();
249 let mut f = |n, e: Expression| f(&SmolStr::new_static(n), e.into());
250 None.or_else(|| f("self", Expression::ElementReference(Rc::downgrade(last?))))
251 .or_else(|| {
252 let len = ctx.component_scope.len();
253 if len >= 2 {
254 f(
255 "parent",
256 Expression::ElementReference(Rc::downgrade(&ctx.component_scope[len - 2]))
257 .into(),
258 )
259 } else {
260 None
261 }
262 })
263 .or_else(|| f("true", Expression::BoolLiteral(true)))
264 .or_else(|| f("false", Expression::BoolLiteral(false)))
265 }
267}
268
269struct IdLookup;
270impl LookupObject for IdLookup {
271 fn for_each_entry<R>(
272 &self,
273 ctx: &LookupCtx,
274 f: &mut impl FnMut(&SmolStr, LookupResult) -> Option<R>,
275 ) -> Option<R> {
276 fn visit<R>(
277 root: &ElementRc,
278 f: &mut impl FnMut(&SmolStr, LookupResult) -> Option<R>,
279 ) -> Option<R> {
280 if !root.borrow().id.is_empty() {
281 if let Some(r) =
282 f(&root.borrow().id, Expression::ElementReference(Rc::downgrade(root)).into())
283 {
284 return Some(r);
285 }
286 }
287 for x in &root.borrow().children {
288 if x.borrow().repeated.is_some() {
289 continue;
290 }
291 if let Some(r) = visit(x, f) {
292 return Some(r);
293 }
294 }
295 None
296 }
297 for e in ctx.component_scope.iter().rev() {
298 if e.borrow().repeated.is_some() {
299 if let Some(r) = visit(e, f) {
300 return Some(r);
301 }
302 }
303 }
304 if let Some(root) = ctx.component_scope.first() {
305 if let Some(r) = visit(root, f) {
306 return Some(r);
307 }
308 }
309 None
310 }
311 }
313
314pub struct InScopeLookup;
316impl InScopeLookup {
317 fn visit_scope<R>(
318 ctx: &LookupCtx,
319 mut visit_entry: impl FnMut(&SmolStr, LookupResult) -> Option<R>,
320 mut visit_legacy_scope: impl FnMut(&ElementRc) -> Option<R>,
321 mut visit_scope: impl FnMut(&ElementRc) -> Option<R>,
322 ) -> Option<R> {
323 let is_legacy = ctx.is_legacy_component();
324 for (idx, elem) in ctx.component_scope.iter().rev().enumerate() {
325 if let Some(repeated) = &elem.borrow().repeated {
326 if !repeated.index_id.is_empty() {
327 if let Some(r) = visit_entry(
328 &repeated.index_id,
329 Expression::RepeaterIndexReference { element: Rc::downgrade(elem) }.into(),
330 ) {
331 return Some(r);
332 }
333 }
334 if !repeated.model_data_id.is_empty() {
335 if let Some(r) = visit_entry(
336 &repeated.model_data_id,
337 Expression::RepeaterModelReference { element: Rc::downgrade(elem) }.into(),
338 ) {
339 return Some(r);
340 }
341 }
342 }
343
344 if is_legacy {
345 if elem.borrow().repeated.is_some()
346 || idx == 0
347 || idx == ctx.component_scope.len() - 1
348 {
349 if let Some(r) = visit_legacy_scope(elem) {
350 return Some(r);
351 }
352 }
353 } else if let Some(r) = visit_scope(elem) {
354 return Some(r);
355 }
356 }
357 None
358 }
359}
360impl LookupObject for InScopeLookup {
361 fn for_each_entry<R>(
362 &self,
363 ctx: &LookupCtx,
364 f: &mut impl FnMut(&SmolStr, LookupResult) -> Option<R>,
365 ) -> Option<R> {
366 let f = RefCell::new(f);
367 Self::visit_scope(
368 ctx,
369 |str, r| f.borrow_mut()(str, r),
370 |elem| elem.for_each_entry(ctx, *f.borrow_mut()),
371 |elem| {
372 for (name, prop) in &elem.borrow().property_declarations {
373 let e = expression_from_reference(
374 NamedReference::new(elem, name.clone()),
375 &prop.property_type,
376 None,
377 );
378 if let Some(r) = f.borrow_mut()(name, e) {
379 return Some(r);
380 }
381 }
382 None
383 },
384 )
385 }
386
387 fn lookup(&self, ctx: &LookupCtx, name: &SmolStr) -> Option<LookupResult> {
388 if name.is_empty() {
389 return None;
390 }
391 Self::visit_scope(
392 ctx,
393 |str, r| (str == name).then_some(r),
394 |elem| elem.lookup(ctx, name),
395 |elem| {
396 elem.borrow().property_declarations.get(name).map(|prop| {
397 expression_from_reference(
398 NamedReference::new(elem, name.clone()),
399 &prop.property_type,
400 None,
401 )
402 })
403 },
404 )
405 }
406}
407
408impl LookupObject for ElementRc {
409 fn for_each_entry<R>(
410 &self,
411 ctx: &LookupCtx,
412 f: &mut impl FnMut(&SmolStr, LookupResult) -> Option<R>,
413 ) -> Option<R> {
414 for (name, prop) in &self.borrow().property_declarations {
415 let deprecated = match check_deprecated_stylemetrics(self, ctx, name) {
416 StyleMetricsPropertyUse::Acceptable => None,
417 StyleMetricsPropertyUse::Deprecated(msg) => Some(msg),
418 StyleMetricsPropertyUse::Unacceptable => continue, };
420
421 let r = expression_from_reference(
422 NamedReference::new(self, name.clone()),
423 &prop.property_type,
424 deprecated,
425 );
426 if let Some(r) = f(name, r) {
427 return Some(r);
428 }
429 }
430 let list = self.borrow().base_type.property_list();
431 for (name, ty) in list {
432 let e = expression_from_reference(NamedReference::new(self, name.clone()), &ty, None);
433 if let Some(r) = f(&name, e) {
434 return Some(r);
435 }
436 }
437 if !(matches!(self.borrow().base_type, ElementType::Global)) {
438 for (name, ty, _) in crate::typeregister::reserved_properties() {
439 let name = SmolStr::new_static(name);
440 let e =
441 expression_from_reference(NamedReference::new(self, name.clone()), &ty, None);
442 if let Some(r) = f(&name, e) {
443 return Some(r);
444 }
445 }
446 }
447 None
448 }
449
450 fn lookup(&self, ctx: &LookupCtx, name: &SmolStr) -> Option<LookupResult> {
451 let lookup_result = self.borrow().lookup_property(name);
452 if lookup_result.property_type != Type::Invalid
453 && (lookup_result.is_local_to_component
454 || lookup_result.property_visibility != PropertyVisibility::Private)
455 {
456 let deprecated = (lookup_result.resolved_name != name.as_str())
457 .then(|| Some(lookup_result.resolved_name.to_string()))
458 .or_else(|| match check_deprecated_stylemetrics(self, ctx, name) {
459 StyleMetricsPropertyUse::Acceptable => Some(None),
460 StyleMetricsPropertyUse::Deprecated(msg) => Some(Some(msg)),
461 StyleMetricsPropertyUse::Unacceptable => None,
462 })?;
463 Some(expression_from_reference(
464 NamedReference::new(self, lookup_result.resolved_name.to_smolstr()),
465 &lookup_result.property_type,
466 deprecated,
467 ))
468 } else {
469 None
470 }
471 }
472}
473
474pub enum StyleMetricsPropertyUse {
476 Acceptable,
478 Deprecated(String),
481 Unacceptable,
483}
484
485pub fn check_deprecated_stylemetrics(
486 elem: &ElementRc,
487 ctx: &LookupCtx<'_>,
488 name: &SmolStr,
489) -> StyleMetricsPropertyUse {
490 let borrow = elem.borrow();
491
492 let is_style_metrics_prop = matches!(
493 borrow.enclosing_component.upgrade().unwrap().id.as_str(),
494 "StyleMetrics" | "NativeStyleMetrics"
495 );
496
497 (!ctx.type_register.expose_internal_types
498 && is_style_metrics_prop
499 && borrow
500 .debug
501 .first()
502 .and_then(|x| x.node.source_file())
503 .map_or(true, |x| x.path().starts_with("builtin:"))
504 && !name.starts_with("layout-"))
505 .then(|| format!("Palette.{name}"))
506 .map_or(StyleMetricsPropertyUse::Acceptable, |msg| {
507 if is_style_metrics_prop && name == "style-name" {
508 StyleMetricsPropertyUse::Unacceptable
509 } else {
510 StyleMetricsPropertyUse::Deprecated(msg)
511 }
512 })
513}
514
515fn expression_from_reference(
516 n: NamedReference,
517 ty: &Type,
518 deprecated: Option<String>,
519) -> LookupResult {
520 match ty {
521 Type::Callback { .. } => Callable::Callback(n).into(),
522 Type::InferredCallback => Callable::Callback(n).into(),
523 Type::Function { .. } => Callable::Function(n).into(),
524 _ => LookupResult::Expression { expression: Expression::PropertyReference(n), deprecated },
525 }
526}
527
528struct LookupType;
530impl LookupObject for LookupType {
531 fn for_each_entry<R>(
532 &self,
533 ctx: &LookupCtx,
534 f: &mut impl FnMut(&SmolStr, LookupResult) -> Option<R>,
535 ) -> Option<R> {
536 for (name, ty) in ctx.type_register.all_types() {
537 if let Some(r) = Self::from_type(ty).and_then(|e| f(&name, e)) {
538 return Some(r);
539 }
540 }
541 for (name, ty) in ctx.type_register.all_elements() {
542 if let Some(r) = Self::from_element(ty, ctx, &name).and_then(|e| f(&name, e)) {
543 return Some(r);
544 }
545 }
546 None
547 }
548
549 fn lookup(&self, ctx: &LookupCtx, name: &SmolStr) -> Option<LookupResult> {
550 Self::from_type(ctx.type_register.lookup(name))
551 .or_else(|| Self::from_element(ctx.type_register.lookup_element(name).ok()?, ctx, name))
552 }
553}
554impl LookupType {
555 fn from_type(ty: Type) -> Option<LookupResult> {
556 match ty {
557 Type::Enumeration(e) => Some(LookupResult::Enumeration(e)),
558 _ => None,
559 }
560 }
561
562 fn from_element(el: ElementType, ctx: &LookupCtx, name: &str) -> Option<LookupResult> {
563 match el {
564 ElementType::Component(c) if c.is_global() => {
565 if c.root_element
567 .borrow()
568 .builtin_type()
569 .is_some_and(|x| x.is_internal && x.name == name)
570 && !ctx.type_register.expose_internal_types
571 {
572 None
573 } else {
574 Some(Expression::ElementReference(Rc::downgrade(&c.root_element)).into())
575 }
576 }
577 _ => None,
578 }
579 }
580}
581
582pub struct ReturnTypeSpecificLookup;
584impl LookupObject for ReturnTypeSpecificLookup {
585 fn for_each_entry<R>(
586 &self,
587 ctx: &LookupCtx,
588 f: &mut impl FnMut(&SmolStr, LookupResult) -> Option<R>,
589 ) -> Option<R> {
590 match ctx.return_type() {
591 Type::Color => ColorSpecific.for_each_entry(ctx, f),
592 Type::Brush => ColorSpecific.for_each_entry(ctx, f),
593 Type::Easing => EasingSpecific.for_each_entry(ctx, f),
594 Type::Enumeration(enumeration) => enumeration.clone().for_each_entry(ctx, f),
595 _ => None,
596 }
597 }
598
599 fn lookup(&self, ctx: &LookupCtx, name: &SmolStr) -> Option<LookupResult> {
600 match ctx.return_type() {
601 Type::Color => ColorSpecific.lookup(ctx, name),
602 Type::Brush => ColorSpecific.lookup(ctx, name),
603 Type::Easing => EasingSpecific.lookup(ctx, name),
604 Type::Enumeration(enumeration) => enumeration.clone().lookup(ctx, name),
605 _ => None,
606 }
607 }
608}
609
610struct ColorSpecific;
611impl LookupObject for ColorSpecific {
612 fn for_each_entry<R>(
613 &self,
614 _ctx: &LookupCtx,
615 f: &mut impl FnMut(&SmolStr, LookupResult) -> Option<R>,
616 ) -> Option<R> {
617 for (name, c) in named_colors::named_colors().iter() {
618 if let Some(r) = f(&SmolStr::new_static(name), Self::as_result(*c)) {
619 return Some(r);
620 }
621 }
622 None
623 }
624 fn lookup(&self, _ctx: &LookupCtx, name: &SmolStr) -> Option<LookupResult> {
625 named_colors::named_colors().get(name.as_str()).map(|c| Self::as_result(*c))
626 }
627}
628impl ColorSpecific {
629 fn as_result(value: u32) -> LookupResult {
630 Expression::Cast {
631 from: Box::new(Expression::NumberLiteral(value as f64, Unit::None)),
632 to: Type::Color,
633 }
634 .into()
635 }
636}
637
638struct KeysLookup;
639
640macro_rules! special_keys_lookup {
641 ($($char:literal # $name:ident # $($qt:ident)|* # $($winit:ident $(($_pos:ident))?)|* # $($_xkb:ident)|*;)*) => {
642 impl LookupObject for KeysLookup {
643 fn for_each_entry<R>(
644 &self,
645 _ctx: &LookupCtx,
646 f: &mut impl FnMut(&SmolStr, LookupResult) -> Option<R>,
647 ) -> Option<R> {
648 None
649 $(.or_else(|| {
650 let mut tmp = [0; 4];
651 f(&SmolStr::new_static(stringify!($name)), Expression::StringLiteral(SmolStr::new_inline($char.encode_utf8(&mut tmp))).into())
652 }))*
653 }
654 }
655 };
656}
657
658i_slint_common::for_each_special_keys!(special_keys_lookup);
659
660struct EasingSpecific;
661impl LookupObject for EasingSpecific {
662 fn for_each_entry<R>(
663 &self,
664 _ctx: &LookupCtx,
665 f: &mut impl FnMut(&SmolStr, LookupResult) -> Option<R>,
666 ) -> Option<R> {
667 use EasingCurve::CubicBezier;
668 let mut curve = |n, e| f(&SmolStr::new_static(n), Expression::EasingCurve(e).into());
669 let r = None
670 .or_else(|| curve("linear", EasingCurve::Linear))
671 .or_else(|| curve("ease-in-quad", CubicBezier(0.11, 0.0, 0.5, 0.0)))
672 .or_else(|| curve("ease-out-quad", CubicBezier(0.5, 1.0, 0.89, 1.0)))
673 .or_else(|| curve("ease-in-out-quad", CubicBezier(0.45, 0.0, 0.55, 1.0)))
674 .or_else(|| curve("ease", CubicBezier(0.25, 0.1, 0.25, 1.0)))
675 .or_else(|| curve("ease-in", CubicBezier(0.42, 0.0, 1.0, 1.0)))
676 .or_else(|| curve("ease-in-out", CubicBezier(0.42, 0.0, 0.58, 1.0)))
677 .or_else(|| curve("ease-out", CubicBezier(0.0, 0.0, 0.58, 1.0)))
678 .or_else(|| curve("ease-in-quart", CubicBezier(0.5, 0.0, 0.75, 0.0)))
679 .or_else(|| curve("ease-out-quart", CubicBezier(0.25, 1.0, 0.5, 1.0)))
680 .or_else(|| curve("ease-in-out-quart", CubicBezier(0.76, 0.0, 0.24, 1.0)))
681 .or_else(|| curve("ease-in-quint", CubicBezier(0.64, 0.0, 0.78, 0.0)))
682 .or_else(|| curve("ease-out-quint", CubicBezier(0.22, 1.0, 0.36, 1.0)))
683 .or_else(|| curve("ease-in-out-quint", CubicBezier(0.83, 0.0, 0.17, 1.0)))
684 .or_else(|| curve("ease-in-expo", CubicBezier(0.7, 0.0, 0.84, 0.0)))
685 .or_else(|| curve("ease-out-expo", CubicBezier(0.16, 1.0, 0.3, 1.0)))
686 .or_else(|| curve("ease-in-out-expo", CubicBezier(0.87, 0.0, 0.13, 1.0)))
687 .or_else(|| curve("ease-in-back", CubicBezier(0.36, 0.0, 0.66, -0.56)))
688 .or_else(|| curve("ease-out-back", CubicBezier(0.34, 1.56, 0.64, 1.0)))
689 .or_else(|| curve("ease-in-out-back", CubicBezier(0.68, -0.6, 0.32, 1.6)))
690 .or_else(|| curve("ease-in-sine", CubicBezier(0.12, 0.0, 0.39, 0.0)))
691 .or_else(|| curve("ease-out-sine", CubicBezier(0.61, 1.0, 0.88, 1.0)))
692 .or_else(|| curve("ease-in-out-sine", CubicBezier(0.37, 0.0, 0.63, 1.0)))
693 .or_else(|| curve("ease-in-circ", CubicBezier(0.55, 0.0, 1.0, 0.45)))
694 .or_else(|| curve("ease-out-circ", CubicBezier(0.0, 0.55, 0.45, 1.0)))
695 .or_else(|| curve("ease-in-out-circ", CubicBezier(0.85, 0.0, 0.15, 1.0)))
696 .or_else(|| curve("ease-in-elastic", EasingCurve::EaseInElastic))
697 .or_else(|| curve("ease-out-elastic", EasingCurve::EaseOutElastic))
698 .or_else(|| curve("ease-in-out-elastic", EasingCurve::EaseInOutElastic))
699 .or_else(|| curve("ease-in-bounce", EasingCurve::EaseInBounce))
700 .or_else(|| curve("ease-out-bounce", EasingCurve::EaseOutBounce))
701 .or_else(|| curve("ease-in-out-bounce", EasingCurve::EaseInOutBounce));
702 r.or_else(|| {
703 f(&SmolStr::new_static("cubic-bezier"), BuiltinMacroFunction::CubicBezier.into())
704 })
705 }
706}
707
708impl LookupObject for Rc<Enumeration> {
709 fn for_each_entry<R>(
710 &self,
711 _ctx: &LookupCtx,
712 f: &mut impl FnMut(&SmolStr, LookupResult) -> Option<R>,
713 ) -> Option<R> {
714 for (value, name) in self.values.iter().enumerate() {
715 if let Some(r) = f(
716 name,
717 Expression::EnumerationValue(EnumerationValue { value, enumeration: self.clone() })
718 .into(),
719 ) {
720 return Some(r);
721 }
722 }
723 None
724 }
725}
726
727struct MathFunctions;
728impl LookupObject for MathFunctions {
729 fn for_each_entry<R>(
730 &self,
731 _ctx: &LookupCtx,
732 f: &mut impl FnMut(&SmolStr, LookupResult) -> Option<R>,
733 ) -> Option<R> {
734 let mut f = |n, e| f(&SmolStr::new_static(n), e);
735 let b = |b| LookupResult::from(Callable::Builtin(b));
736 None.or_else(|| f("mod", BuiltinMacroFunction::Mod.into()))
737 .or_else(|| f("round", b(BuiltinFunction::Round)))
738 .or_else(|| f("ceil", b(BuiltinFunction::Ceil)))
739 .or_else(|| f("floor", b(BuiltinFunction::Floor)))
740 .or_else(|| f("clamp", BuiltinMacroFunction::Clamp.into()))
741 .or_else(|| f("abs", BuiltinMacroFunction::Abs.into()))
742 .or_else(|| f("sqrt", b(BuiltinFunction::Sqrt)))
743 .or_else(|| f("max", BuiltinMacroFunction::Max.into()))
744 .or_else(|| f("min", BuiltinMacroFunction::Min.into()))
745 .or_else(|| f("sin", b(BuiltinFunction::Sin)))
746 .or_else(|| f("cos", b(BuiltinFunction::Cos)))
747 .or_else(|| f("tan", b(BuiltinFunction::Tan)))
748 .or_else(|| f("asin", b(BuiltinFunction::ASin)))
749 .or_else(|| f("acos", b(BuiltinFunction::ACos)))
750 .or_else(|| f("atan", b(BuiltinFunction::ATan)))
751 .or_else(|| f("atan2", b(BuiltinFunction::ATan2)))
752 .or_else(|| f("log", b(BuiltinFunction::Log)))
753 .or_else(|| f("pow", b(BuiltinFunction::Pow)))
754 }
755}
756
757struct SlintInternal;
758impl LookupObject for SlintInternal {
759 fn for_each_entry<R>(
760 &self,
761 ctx: &LookupCtx,
762 f: &mut impl FnMut(&SmolStr, LookupResult) -> Option<R>,
763 ) -> Option<R> {
764 let sl = || ctx.current_token.as_ref().map(|t| t.to_source_location());
765 let mut f = |n, e: LookupResult| f(&SmolStr::new_static(n), e);
766 let b = |b| LookupResult::from(Callable::Builtin(b));
767 None.or_else(|| {
768 let style = ctx.type_loader.and_then(|tl| tl.compiler_config.style.as_ref());
769 f(
770 "color-scheme",
771 if style.is_some_and(|s| s.ends_with("-light")) {
772 let e = crate::typeregister::BUILTIN.with(|e| e.enums.ColorScheme.clone());
773 Expression::EnumerationValue(e.try_value_from_string("light").unwrap())
774 } else if style.is_some_and(|s| s.ends_with("-dark")) {
775 let e = crate::typeregister::BUILTIN.with(|e| e.enums.ColorScheme.clone());
776 Expression::EnumerationValue(e.try_value_from_string("dark").unwrap())
777 } else {
778 Expression::FunctionCall {
779 function: BuiltinFunction::ColorScheme.into(),
780 arguments: vec![],
781 source_location: sl(),
782 }
783 }
784 .into(),
785 )
786 })
787 .or_else(|| {
788 f(
789 "use-24-hour-format",
790 Expression::FunctionCall {
791 function: BuiltinFunction::Use24HourFormat.into(),
792 arguments: vec![],
793 source_location: sl(),
794 }
795 .into(),
796 )
797 })
798 .or_else(|| f("month-day-count", b(BuiltinFunction::MonthDayCount)))
799 .or_else(|| f("month-offset", b(BuiltinFunction::MonthOffset)))
800 .or_else(|| f("format-date", b(BuiltinFunction::FormatDate)))
801 .or_else(|| f("date-now", b(BuiltinFunction::DateNow)))
802 .or_else(|| f("valid-date", b(BuiltinFunction::ValidDate)))
803 .or_else(|| f("parse-date", b(BuiltinFunction::ParseDate)))
804 }
805}
806
807struct ColorFunctions;
808impl LookupObject for ColorFunctions {
809 fn for_each_entry<R>(
810 &self,
811 _ctx: &LookupCtx,
812 f: &mut impl FnMut(&SmolStr, LookupResult) -> Option<R>,
813 ) -> Option<R> {
814 let mut f = |n, m| f(&SmolStr::new_static(n), LookupResult::from(m));
815 None.or_else(|| f("rgb", BuiltinMacroFunction::Rgb))
816 .or_else(|| f("rgba", BuiltinMacroFunction::Rgb))
817 .or_else(|| f("hsv", BuiltinMacroFunction::Hsv))
818 }
819}
820
821struct BuiltinFunctionLookup;
822impl LookupObject for BuiltinFunctionLookup {
823 fn for_each_entry<R>(
824 &self,
825 ctx: &LookupCtx,
826 f: &mut impl FnMut(&SmolStr, LookupResult) -> Option<R>,
827 ) -> Option<R> {
828 (MathFunctions, ColorFunctions)
829 .for_each_entry(ctx, f)
830 .or_else(|| f(&SmolStr::new_static("debug"), BuiltinMacroFunction::Debug.into()))
831 .or_else(|| {
832 f(&SmolStr::new_static("animation-tick"), BuiltinFunction::AnimationTick.into())
833 })
834 }
835}
836
837struct BuiltinNamespaceLookup;
838impl LookupObject for BuiltinNamespaceLookup {
839 fn for_each_entry<R>(
840 &self,
841 ctx: &LookupCtx,
842 f: &mut impl FnMut(&SmolStr, LookupResult) -> Option<R>,
843 ) -> Option<R> {
844 let mut f = |s, res| f(&SmolStr::new_static(s), res);
845 None.or_else(|| f("Colors", LookupResult::Namespace(BuiltinNamespace::Colors)))
846 .or_else(|| f("Math", LookupResult::Namespace(BuiltinNamespace::Math)))
847 .or_else(|| f("Key", LookupResult::Namespace(BuiltinNamespace::Key)))
848 .or_else(|| {
849 if ctx.type_register.expose_internal_types {
850 f("SlintInternal", LookupResult::Namespace(BuiltinNamespace::SlintInternal))
851 } else {
852 None
853 }
854 })
855 }
856}
857
858pub fn global_lookup() -> impl LookupObject {
859 (
860 ArgumentsLookup,
861 (
862 SpecialIdLookup,
863 (
864 IdLookup,
865 (
866 InScopeLookup,
867 (
868 LookupType,
869 (BuiltinNamespaceLookup, (ReturnTypeSpecificLookup, BuiltinFunctionLookup)),
870 ),
871 ),
872 ),
873 ),
874 )
875}
876
877impl LookupObject for Expression {
878 fn for_each_entry<R>(
879 &self,
880 ctx: &LookupCtx,
881 f: &mut impl FnMut(&SmolStr, LookupResult) -> Option<R>,
882 ) -> Option<R> {
883 match self {
884 Expression::ElementReference(e) => e.upgrade().unwrap().for_each_entry(ctx, f),
885 _ => match self.ty() {
886 Type::Struct(s) => {
887 for name in s.fields.keys() {
888 if let Some(r) = f(
889 name,
890 Expression::StructFieldAccess {
891 base: Box::new(self.clone()),
892 name: name.clone(),
893 }
894 .into(),
895 ) {
896 return Some(r);
897 }
898 }
899 None
900 }
901 Type::String => StringExpression(self).for_each_entry(ctx, f),
902 Type::Brush | Type::Color => ColorExpression(self).for_each_entry(ctx, f),
903 Type::Image => ImageExpression(self).for_each_entry(ctx, f),
904 Type::Array(_) => ArrayExpression(self).for_each_entry(ctx, f),
905 Type::Float32 | Type::Int32 | Type::Percent => {
906 NumberExpression(self).for_each_entry(ctx, f)
907 }
908 ty if ty.as_unit_product().is_some() => {
909 NumberWithUnitExpression(self).for_each_entry(ctx, f)
910 }
911 _ => None,
912 },
913 }
914 }
915
916 fn lookup(&self, ctx: &LookupCtx, name: &SmolStr) -> Option<LookupResult> {
917 match self {
918 Expression::ElementReference(e) => e.upgrade().unwrap().lookup(ctx, name),
919 _ => match self.ty() {
920 Type::Struct(s) => s.fields.contains_key(name).then(|| {
921 LookupResult::from(Expression::StructFieldAccess {
922 base: Box::new(self.clone()),
923 name: name.clone(),
924 })
925 }),
926 Type::String => StringExpression(self).lookup(ctx, name),
927 Type::Brush | Type::Color => ColorExpression(self).lookup(ctx, name),
928 Type::Image => ImageExpression(self).lookup(ctx, name),
929 Type::Array(_) => ArrayExpression(self).lookup(ctx, name),
930 Type::Float32 | Type::Int32 | Type::Percent => {
931 NumberExpression(self).lookup(ctx, name)
932 }
933 ty if ty.as_unit_product().is_some() => {
934 NumberWithUnitExpression(self).lookup(ctx, name)
935 }
936 _ => None,
937 },
938 }
939 }
940}
941
942struct StringExpression<'a>(&'a Expression);
943impl LookupObject for StringExpression<'_> {
944 fn for_each_entry<R>(
945 &self,
946 ctx: &LookupCtx,
947 f: &mut impl FnMut(&SmolStr, LookupResult) -> Option<R>,
948 ) -> Option<R> {
949 let member_function = |f: BuiltinFunction| {
950 LookupResult::Callable(LookupResultCallable::MemberFunction {
951 base: self.0.clone(),
952 base_node: ctx.current_token.clone(), member: LookupResultCallable::Callable(Callable::Builtin(f)).into(),
954 })
955 };
956 let function_call = |f: BuiltinFunction| {
957 LookupResult::from(Expression::FunctionCall {
958 function: Callable::Builtin(f),
959 source_location: ctx.current_token.as_ref().map(|t| t.to_source_location()),
960 arguments: vec![self.0.clone()],
961 })
962 };
963
964 let mut f = |s, res| f(&SmolStr::new_static(s), res);
965 None.or_else(|| f("is-float", member_function(BuiltinFunction::StringIsFloat)))
966 .or_else(|| f("to-float", member_function(BuiltinFunction::StringToFloat)))
967 .or_else(|| f("is-empty", function_call(BuiltinFunction::StringIsEmpty)))
968 .or_else(|| f("character-count", function_call(BuiltinFunction::StringCharacterCount)))
969 .or_else(|| f("to-lowercase", member_function(BuiltinFunction::StringToLowercase)))
970 .or_else(|| f("to-uppercase", member_function(BuiltinFunction::StringToUppercase)))
971 }
972}
973struct ColorExpression<'a>(&'a Expression);
974impl LookupObject for ColorExpression<'_> {
975 fn for_each_entry<R>(
976 &self,
977 ctx: &LookupCtx,
978 f: &mut impl FnMut(&SmolStr, LookupResult) -> Option<R>,
979 ) -> Option<R> {
980 let member_function = |f: BuiltinFunction| {
981 let base = if f == BuiltinFunction::ColorHsvaStruct && self.0.ty() == Type::Brush {
982 Expression::Cast { from: Box::new(self.0.clone()), to: Type::Color }
983 } else {
984 self.0.clone()
985 };
986 LookupResult::Callable(LookupResultCallable::MemberFunction {
987 base,
988 base_node: ctx.current_token.clone(), member: Box::new(LookupResultCallable::Callable(Callable::Builtin(f))),
990 })
991 };
992 let field_access = |f: &'static str| {
993 let base = if self.0.ty() == Type::Brush {
994 Expression::Cast { from: Box::new(self.0.clone()), to: Type::Color }
995 } else {
996 self.0.clone()
997 };
998 LookupResult::from(Expression::StructFieldAccess {
999 base: Box::new(Expression::FunctionCall {
1000 function: BuiltinFunction::ColorRgbaStruct.into(),
1001 source_location: ctx.current_token.as_ref().map(|t| t.to_source_location()),
1002 arguments: vec![base],
1003 }),
1004 name: SmolStr::new_static(f),
1005 })
1006 };
1007
1008 let mut f = |s, res| f(&SmolStr::new_static(s), res);
1009 None.or_else(|| f("red", field_access("red")))
1010 .or_else(|| f("green", field_access("green")))
1011 .or_else(|| f("blue", field_access("blue")))
1012 .or_else(|| f("alpha", field_access("alpha")))
1013 .or_else(|| f("to-hsv", member_function(BuiltinFunction::ColorHsvaStruct)))
1014 .or_else(|| f("brighter", member_function(BuiltinFunction::ColorBrighter)))
1015 .or_else(|| f("darker", member_function(BuiltinFunction::ColorDarker)))
1016 .or_else(|| f("transparentize", member_function(BuiltinFunction::ColorTransparentize)))
1017 .or_else(|| f("with-alpha", member_function(BuiltinFunction::ColorWithAlpha)))
1018 .or_else(|| f("mix", member_function(BuiltinFunction::ColorMix)))
1019 }
1020}
1021
1022struct ImageExpression<'a>(&'a Expression);
1023impl LookupObject for ImageExpression<'_> {
1024 fn for_each_entry<R>(
1025 &self,
1026 ctx: &LookupCtx,
1027 f: &mut impl FnMut(&SmolStr, LookupResult) -> Option<R>,
1028 ) -> Option<R> {
1029 let field_access = |f: &str| {
1030 LookupResult::from(Expression::StructFieldAccess {
1031 base: Box::new(Expression::FunctionCall {
1032 function: BuiltinFunction::ImageSize.into(),
1033 source_location: ctx.current_token.as_ref().map(|t| t.to_source_location()),
1034 arguments: vec![self.0.clone()],
1035 }),
1036 name: f.into(),
1037 })
1038 };
1039 let mut f = |s, res| f(&SmolStr::new_static(s), res);
1040 None.or_else(|| f("width", field_access("width")))
1041 .or_else(|| f("height", field_access("height")))
1042 }
1043}
1044
1045struct ArrayExpression<'a>(&'a Expression);
1046impl LookupObject for ArrayExpression<'_> {
1047 fn for_each_entry<R>(
1048 &self,
1049 ctx: &LookupCtx,
1050 f: &mut impl FnMut(&SmolStr, LookupResult) -> Option<R>,
1051 ) -> Option<R> {
1052 let member_function = |f: BuiltinFunction| {
1053 LookupResult::from(Expression::FunctionCall {
1054 function: Callable::Builtin(f),
1055 source_location: ctx.current_token.as_ref().map(|t| t.to_source_location()),
1056 arguments: vec![self.0.clone()],
1057 })
1058 };
1059 None.or_else(|| {
1060 f(&SmolStr::new_static("length"), member_function(BuiltinFunction::ArrayLength))
1061 })
1062 }
1063}
1064
1065struct NumberExpression<'a>(&'a Expression);
1067impl LookupObject for NumberExpression<'_> {
1068 fn for_each_entry<R>(
1069 &self,
1070 ctx: &LookupCtx,
1071 f: &mut impl FnMut(&SmolStr, LookupResult) -> Option<R>,
1072 ) -> Option<R> {
1073 let member_function = |f: BuiltinFunction| {
1074 LookupResult::Callable(LookupResultCallable::MemberFunction {
1075 base: self.0.clone(),
1076 base_node: ctx.current_token.clone(), member: LookupResultCallable::Callable(Callable::Builtin(f)).into(),
1078 })
1079 };
1080
1081 let mut f2 = |s, res| f(&SmolStr::new_static(s), res);
1082 None.or_else(|| f2("round", member_function(BuiltinFunction::Round)))
1083 .or_else(|| f2("ceil", member_function(BuiltinFunction::Ceil)))
1084 .or_else(|| f2("floor", member_function(BuiltinFunction::Floor)))
1085 .or_else(|| f2("sqrt", member_function(BuiltinFunction::Sqrt)))
1086 .or_else(|| f2("asin", member_function(BuiltinFunction::ASin)))
1087 .or_else(|| f2("acos", member_function(BuiltinFunction::ACos)))
1088 .or_else(|| f2("atan", member_function(BuiltinFunction::ATan)))
1089 .or_else(|| f2("log", member_function(BuiltinFunction::Log)))
1090 .or_else(|| f2("pow", member_function(BuiltinFunction::Pow)))
1091 .or_else(|| f2("to-fixed", member_function(BuiltinFunction::ToFixed)))
1092 .or_else(|| f2("to-precision", member_function(BuiltinFunction::ToPrecision)))
1093 .or_else(|| NumberWithUnitExpression(self.0).for_each_entry(ctx, f))
1094 }
1095}
1096
1097struct NumberWithUnitExpression<'a>(&'a Expression);
1099impl LookupObject for NumberWithUnitExpression<'_> {
1100 fn for_each_entry<R>(
1101 &self,
1102 ctx: &LookupCtx,
1103 f: &mut impl FnMut(&SmolStr, LookupResult) -> Option<R>,
1104 ) -> Option<R> {
1105 let member_macro = |f: BuiltinMacroFunction| {
1106 LookupResult::Callable(LookupResultCallable::MemberFunction {
1107 base: self.0.clone(),
1108 base_node: ctx.current_token.clone(), member: Box::new(LookupResultCallable::Macro(f)),
1110 })
1111 };
1112
1113 let mut f = |s, res| f(&SmolStr::new_static(s), res);
1114 None.or_else(|| f("mod", member_macro(BuiltinMacroFunction::Mod)))
1115 .or_else(|| f("clamp", member_macro(BuiltinMacroFunction::Clamp)))
1116 .or_else(|| f("abs", member_macro(BuiltinMacroFunction::Abs)))
1117 .or_else(|| f("max", member_macro(BuiltinMacroFunction::Max)))
1118 .or_else(|| f("min", member_macro(BuiltinMacroFunction::Min)))
1119 .or_else(|| {
1120 if self.0.ty() != Type::Angle {
1121 return None;
1122 }
1123 let member_function = |f: BuiltinFunction| {
1124 LookupResult::Callable(LookupResultCallable::MemberFunction {
1125 base: self.0.clone(),
1126 base_node: ctx.current_token.clone(), member: Box::new(LookupResultCallable::Callable(Callable::Builtin(f))),
1128 })
1129 };
1130 None.or_else(|| f("sin", member_function(BuiltinFunction::Sin)))
1131 .or_else(|| f("cos", member_function(BuiltinFunction::Cos)))
1132 .or_else(|| f("tan", member_function(BuiltinFunction::Tan)))
1133 })
1134 }
1135}