1 | // Copyright © SixtyFPS GmbH <info@slint.dev> |
2 | // SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-1.1 OR LicenseRef-Slint-commercial |
3 | |
4 | /*! |
5 | This module contains the intermediate representation of the code in the form of an object tree |
6 | */ |
7 | |
8 | // cSpell: ignore qualname |
9 | |
10 | use crate::diagnostics::{BuildDiagnostics, SourceLocation, Spanned}; |
11 | use crate::expression_tree::{self, BindingExpression, Expression, Unit}; |
12 | use crate::langtype::{BuiltinElement, Enumeration, NativeClass, Type}; |
13 | use crate::langtype::{ElementType, PropertyLookupResult}; |
14 | use crate::layout::{LayoutConstraints, Orientation}; |
15 | use crate::namedreference::NamedReference; |
16 | use crate::parser; |
17 | use crate::parser::{syntax_nodes, SyntaxKind, SyntaxNode}; |
18 | use crate::typeloader::ImportedTypes; |
19 | use crate::typeregister::TypeRegister; |
20 | use itertools::Either; |
21 | use once_cell::unsync::OnceCell; |
22 | use std::cell::{Cell, RefCell}; |
23 | use std::collections::btree_map::Entry; |
24 | use std::collections::{BTreeMap, HashMap, HashSet}; |
25 | use std::fmt::Display; |
26 | use std::path::PathBuf; |
27 | use std::rc::{Rc, Weak}; |
28 | |
29 | macro_rules! unwrap_or_continue { |
30 | ($e:expr ; $diag:expr) => { |
31 | match $e { |
32 | Some(x) => x, |
33 | None => { |
34 | debug_assert!($diag.has_error()); // error should have been reported at parsing time |
35 | continue; |
36 | } |
37 | } |
38 | }; |
39 | } |
40 | |
41 | /// The full document (a complete file) |
42 | #[derive (Default, Debug)] |
43 | pub struct Document { |
44 | pub node: Option<syntax_nodes::Document>, |
45 | pub inner_components: Vec<Rc<Component>>, |
46 | pub inner_types: Vec<Type>, |
47 | pub root_component: Rc<Component>, |
48 | pub local_registry: TypeRegister, |
49 | /// A list of paths to .ttf/.ttc files that are supposed to be registered on |
50 | /// startup for custom font use. |
51 | pub custom_fonts: Vec<(String, crate::parser::SyntaxToken)>, |
52 | pub exports: Exports, |
53 | } |
54 | |
55 | impl Document { |
56 | pub fn from_node( |
57 | node: syntax_nodes::Document, |
58 | foreign_imports: Vec<ImportedTypes>, |
59 | reexports: Exports, |
60 | diag: &mut BuildDiagnostics, |
61 | parent_registry: &Rc<RefCell<TypeRegister>>, |
62 | ) -> Self { |
63 | debug_assert_eq!(node.kind(), SyntaxKind::Document); |
64 | |
65 | let mut local_registry = TypeRegister::new(parent_registry); |
66 | let mut inner_components = vec![]; |
67 | let mut inner_types = vec![]; |
68 | |
69 | let mut process_component = |
70 | |n: syntax_nodes::Component, |
71 | diag: &mut BuildDiagnostics, |
72 | local_registry: &mut TypeRegister| { |
73 | let compo = Component::from_node(n, diag, local_registry); |
74 | local_registry.add(compo.clone()); |
75 | inner_components.push(compo); |
76 | }; |
77 | let process_struct = |n: syntax_nodes::StructDeclaration, |
78 | diag: &mut BuildDiagnostics, |
79 | local_registry: &mut TypeRegister, |
80 | inner_types: &mut Vec<Type>| { |
81 | let rust_attributes = n.AtRustAttr().map(|child| vec![child.text().to_string()]); |
82 | let mut ty = |
83 | type_struct_from_node(n.ObjectType(), diag, local_registry, rust_attributes); |
84 | if let Type::Struct { name, .. } = &mut ty { |
85 | *name = parser::identifier_text(&n.DeclaredIdentifier()); |
86 | } else { |
87 | assert!(diag.has_error()); |
88 | return; |
89 | } |
90 | local_registry.insert_type(ty.clone()); |
91 | inner_types.push(ty); |
92 | }; |
93 | let process_enum = |n: syntax_nodes::EnumDeclaration, |
94 | diag: &mut BuildDiagnostics, |
95 | local_registry: &mut TypeRegister, |
96 | inner_types: &mut Vec<Type>| { |
97 | let Some(name) = parser::identifier_text(&n.DeclaredIdentifier()) else { |
98 | assert!(diag.has_error()); |
99 | return; |
100 | }; |
101 | let mut existing_names = HashSet::new(); |
102 | let values = n |
103 | .EnumValue() |
104 | .filter_map(|v| { |
105 | let value = parser::identifier_text(&v)?; |
106 | if value == name { |
107 | diag.push_error( |
108 | format!("Enum ' {value}' can't have a value with the same name" ), |
109 | &v, |
110 | ); |
111 | None |
112 | } else if !existing_names.insert(crate::generator::to_pascal_case(&value)) { |
113 | diag.push_error(format!("Duplicated enum value ' {value}'" ), &v); |
114 | None |
115 | } else { |
116 | Some(value) |
117 | } |
118 | }) |
119 | .collect(); |
120 | let en = Enumeration { name: name.clone(), values, default_value: 0, node: Some(n) }; |
121 | let ty = Type::Enumeration(Rc::new(en)); |
122 | local_registry.insert_type_with_name(ty.clone(), name); |
123 | inner_types.push(ty); |
124 | }; |
125 | |
126 | for n in node.children() { |
127 | match n.kind() { |
128 | SyntaxKind::Component => process_component(n.into(), diag, &mut local_registry), |
129 | SyntaxKind::StructDeclaration => { |
130 | process_struct(n.into(), diag, &mut local_registry, &mut inner_types) |
131 | } |
132 | SyntaxKind::EnumDeclaration => { |
133 | process_enum(n.into(), diag, &mut local_registry, &mut inner_types) |
134 | } |
135 | SyntaxKind::ExportsList => { |
136 | for n in n.children() { |
137 | match n.kind() { |
138 | SyntaxKind::Component => { |
139 | process_component(n.into(), diag, &mut local_registry) |
140 | } |
141 | SyntaxKind::StructDeclaration => process_struct( |
142 | n.into(), |
143 | diag, |
144 | &mut local_registry, |
145 | &mut inner_types, |
146 | ), |
147 | SyntaxKind::EnumDeclaration => { |
148 | process_enum(n.into(), diag, &mut local_registry, &mut inner_types) |
149 | } |
150 | _ => {} |
151 | } |
152 | } |
153 | } |
154 | _ => {} |
155 | }; |
156 | } |
157 | let mut exports = Exports::from_node(&node, &inner_components, &local_registry, diag); |
158 | exports.add_reexports(reexports, diag); |
159 | |
160 | let root_component = exports |
161 | .last_exported_component |
162 | .clone() |
163 | .or_else(|| { |
164 | node.ImportSpecifier() |
165 | .last() |
166 | .and_then(|import| { |
167 | crate::typeloader::ImportedName::extract_imported_names(&import).last() |
168 | }) |
169 | .and_then(|import| local_registry.lookup_element(&import.internal_name).ok()) |
170 | .and_then(|c| match c { |
171 | ElementType::Component(c) => Some(c), |
172 | _ => None, |
173 | }) |
174 | }) |
175 | .unwrap_or_default(); |
176 | |
177 | let custom_fonts = foreign_imports |
178 | .into_iter() |
179 | .filter_map(|import| { |
180 | if import.file.ends_with(".ttc" ) |
181 | || import.file.ends_with(".ttf" ) |
182 | || import.file.ends_with(".otf" ) |
183 | { |
184 | let token_path = import.import_uri_token.source_file.path(); |
185 | let import_file_path = PathBuf::from(import.file.clone()); |
186 | let import_file_path = crate::pathutils::join(token_path, &import_file_path) |
187 | .unwrap_or(import_file_path); |
188 | |
189 | // Assume remote urls are valid, we need to load them at run-time (which we currently don't). For |
190 | // local paths we should try to verify the existence and let the developer know ASAP. |
191 | if crate::pathutils::is_url(&import_file_path) |
192 | || crate::fileaccess::load_file(std::path::Path::new(&import_file_path)) |
193 | .is_some() |
194 | { |
195 | Some(( |
196 | import_file_path.to_string_lossy().to_string(), |
197 | import.import_uri_token, |
198 | )) |
199 | } else { |
200 | diag.push_error( |
201 | format!("File \"{}\" not found" , import.file), |
202 | &import.import_uri_token, |
203 | ); |
204 | None |
205 | } |
206 | } else { |
207 | diag.push_error( |
208 | format!("Unsupported foreign import \"{}\"" , import.file), |
209 | &import.import_uri_token, |
210 | ); |
211 | None |
212 | } |
213 | }) |
214 | .collect(); |
215 | |
216 | for local_compo in &inner_components { |
217 | if exports |
218 | .components_or_types |
219 | .iter() |
220 | .filter_map(|(_, exported_compo_or_type)| exported_compo_or_type.as_ref().left()) |
221 | .any(|exported_compo| Rc::ptr_eq(exported_compo, local_compo)) |
222 | { |
223 | continue; |
224 | } |
225 | // Don't warn about these for now - detecting their use can only be done after the resolve_expressions |
226 | // pass. |
227 | if local_compo.is_global() { |
228 | continue; |
229 | } |
230 | // First ref count is in the type registry, the second one in inner_components. Any use of the element |
231 | // would have resulted in another strong reference. |
232 | if Rc::strong_count(local_compo) == 2 { |
233 | diag.push_warning( |
234 | "Component is neither used nor exported" .into(), |
235 | &local_compo.node, |
236 | ) |
237 | } |
238 | } |
239 | |
240 | Document { |
241 | node: Some(node), |
242 | root_component, |
243 | inner_components, |
244 | inner_types, |
245 | local_registry, |
246 | custom_fonts, |
247 | exports, |
248 | } |
249 | } |
250 | } |
251 | |
252 | #[derive (Debug, Clone)] |
253 | pub struct PopupWindow { |
254 | pub component: Rc<Component>, |
255 | pub x: NamedReference, |
256 | pub y: NamedReference, |
257 | pub close_on_click: bool, |
258 | pub parent_element: ElementRc, |
259 | } |
260 | |
261 | type ChildrenInsertionPoint = (ElementRc, syntax_nodes::ChildrenPlaceholder); |
262 | |
263 | /// Used sub types for a root component |
264 | #[derive (Debug, Default)] |
265 | pub struct UsedSubTypes { |
266 | /// All the globals used by the component and its children. |
267 | pub globals: Vec<Rc<Component>>, |
268 | /// All the structs and enums used by the component and its children. |
269 | pub structs_and_enums: Vec<Type>, |
270 | /// All the sub components use by this components and its children, |
271 | /// and the amount of time it is used |
272 | pub sub_components: Vec<Rc<Component>>, |
273 | } |
274 | |
275 | #[derive (Debug, Default, Clone)] |
276 | pub struct InitCode { |
277 | // Code from init callbacks collected from elements |
278 | pub constructor_code: Vec<Expression>, |
279 | /// Code to set the initial focus via forward-focus on the Window |
280 | pub focus_setting_code: Vec<Expression>, |
281 | /// Code to register embedded fonts. |
282 | pub font_registration_code: Vec<Expression>, |
283 | |
284 | /// Code inserted from inlined components, ordered by offset of the place where it was inlined from. This way |
285 | /// we can preserve the order across multiple inlining passes. |
286 | pub inlined_init_code: BTreeMap<usize, Expression>, |
287 | } |
288 | |
289 | impl InitCode { |
290 | pub fn iter(&self) -> impl Iterator<Item = &Expression> { |
291 | self.font_registration_code |
292 | .iter() |
293 | .chain(self.focus_setting_code.iter()) |
294 | .chain(self.constructor_code.iter()) |
295 | .chain(self.inlined_init_code.values()) |
296 | } |
297 | pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut Expression> { |
298 | self.font_registration_code |
299 | .iter_mut() |
300 | .chain(self.focus_setting_code.iter_mut()) |
301 | .chain(self.constructor_code.iter_mut()) |
302 | .chain(self.inlined_init_code.values_mut()) |
303 | } |
304 | } |
305 | |
306 | /// A component is a type in the language which can be instantiated, |
307 | /// Or is materialized for repeated expression. |
308 | #[derive (Default, Debug)] |
309 | pub struct Component { |
310 | pub node: Option<SyntaxNode>, |
311 | pub id: String, |
312 | pub root_element: ElementRc, |
313 | |
314 | /// The parent element within the parent component if this component represents a repeated element |
315 | pub parent_element: Weak<RefCell<Element>>, |
316 | |
317 | /// List of elements that are not attached to the root anymore because they have been |
318 | /// optimized away, but their properties may still be in use |
319 | pub optimized_elements: RefCell<Vec<ElementRc>>, |
320 | |
321 | /// Map of resources that should be embedded in the generated code, indexed by their absolute path on |
322 | /// disk on the build system |
323 | pub embedded_file_resources: |
324 | RefCell<HashMap<String, crate::embedded_resources::EmbeddedResources>>, |
325 | |
326 | /// The layout constraints of the root item |
327 | pub root_constraints: RefCell<LayoutConstraints>, |
328 | |
329 | /// When creating this component and inserting "children", append them to the children of |
330 | /// the element pointer to by this field. |
331 | pub child_insertion_point: RefCell<Option<ChildrenInsertionPoint>>, |
332 | |
333 | pub init_code: RefCell<InitCode>, |
334 | |
335 | /// The list of used extra types used (recursively) by this root component. |
336 | /// (This only make sense on the root component) |
337 | pub used_types: RefCell<UsedSubTypes>, |
338 | pub popup_windows: RefCell<Vec<PopupWindow>>, |
339 | |
340 | /// The names under which this component should be accessible |
341 | /// if it is a global singleton and exported. |
342 | pub exported_global_names: RefCell<Vec<ExportedName>>, |
343 | |
344 | /// The list of properties (name and type) declared as private in the component. |
345 | /// This is used to issue better error in the generated code if the property is used. |
346 | pub private_properties: RefCell<Vec<(String, Type)>>, |
347 | |
348 | /// This is the main entry point for the code generators. Such a component |
349 | /// should have the full API, etc. |
350 | pub is_root_component: Cell<bool>, |
351 | } |
352 | |
353 | impl Component { |
354 | pub fn from_node( |
355 | node: syntax_nodes::Component, |
356 | diag: &mut BuildDiagnostics, |
357 | tr: &TypeRegister, |
358 | ) -> Rc<Self> { |
359 | let mut child_insertion_point = None; |
360 | let is_legacy_syntax = node.child_token(SyntaxKind::ColonEqual).is_some(); |
361 | let c = Component { |
362 | node: Some(node.clone().into()), |
363 | id: parser::identifier_text(&node.DeclaredIdentifier()).unwrap_or_default(), |
364 | root_element: Element::from_node( |
365 | node.Element(), |
366 | "root" .into(), |
367 | if node.child_text(SyntaxKind::Identifier).map_or(false, |t| t == "global" ) { |
368 | ElementType::Global |
369 | } else { |
370 | ElementType::Error |
371 | }, |
372 | &mut child_insertion_point, |
373 | is_legacy_syntax, |
374 | diag, |
375 | tr, |
376 | ), |
377 | child_insertion_point: RefCell::new(child_insertion_point), |
378 | ..Default::default() |
379 | }; |
380 | let c = Rc::new(c); |
381 | let weak = Rc::downgrade(&c); |
382 | recurse_elem(&c.root_element, &(), &mut |e, _| { |
383 | e.borrow_mut().enclosing_component = weak.clone() |
384 | }); |
385 | c |
386 | } |
387 | |
388 | /// This component is a global component introduced with the "global" keyword |
389 | pub fn is_global(&self) -> bool { |
390 | match &self.root_element.borrow().base_type { |
391 | ElementType::Global => true, |
392 | ElementType::Builtin(c) => c.is_global, |
393 | _ => false, |
394 | } |
395 | } |
396 | |
397 | pub fn visible_in_public_api(&self) -> bool { |
398 | if self.is_global() { |
399 | !self.exported_global_names.borrow().is_empty() |
400 | } else { |
401 | self.parent_element.upgrade().is_none() && self.is_root_component.get() |
402 | } |
403 | } |
404 | |
405 | /// Returns the names of aliases to global singletons, exactly as |
406 | /// specified in the .slint markup (not normalized). |
407 | pub fn global_aliases(&self) -> Vec<String> { |
408 | self.exported_global_names |
409 | .borrow() |
410 | .iter() |
411 | .filter(|name| name.as_str() != self.root_element.borrow().id) |
412 | .map(|name| name.original_name()) |
413 | .collect() |
414 | } |
415 | |
416 | pub fn is_sub_component(&self) -> bool { |
417 | !self.is_root_component.get() |
418 | && self.parent_element.upgrade().is_none() |
419 | && !self.is_global() |
420 | } |
421 | |
422 | // Number of repeaters in this component, including sub-components |
423 | pub fn repeater_count(&self) -> u32 { |
424 | let mut count = 0; |
425 | recurse_elem(&self.root_element, &(), &mut |element, _| { |
426 | let element = element.borrow(); |
427 | if let Some(sub_component) = element.sub_component() { |
428 | count += sub_component.repeater_count(); |
429 | } else if element.repeated.is_some() { |
430 | count += 1; |
431 | } |
432 | }); |
433 | count |
434 | } |
435 | } |
436 | |
437 | #[derive (Copy, Clone, Debug, Eq, PartialEq, Default)] |
438 | pub enum PropertyVisibility { |
439 | #[default] |
440 | Private, |
441 | Input, |
442 | Output, |
443 | InOut, |
444 | /// for builtin properties that must be known at compile time and cannot be changed at runtime |
445 | Constexpr, |
446 | /// For functions, not properties |
447 | Public, |
448 | Protected, |
449 | } |
450 | |
451 | impl Display for PropertyVisibility { |
452 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
453 | match self { |
454 | PropertyVisibility::Private => f.write_str(data:"private" ), |
455 | PropertyVisibility::Input => f.write_str(data:"input" ), |
456 | PropertyVisibility::Output => f.write_str(data:"output" ), |
457 | PropertyVisibility::InOut => f.write_str(data:"input output" ), |
458 | PropertyVisibility::Constexpr => f.write_str(data:"constexpr" ), |
459 | PropertyVisibility::Public => f.write_str(data:"public" ), |
460 | PropertyVisibility::Protected => f.write_str(data:"protected" ), |
461 | } |
462 | } |
463 | } |
464 | |
465 | #[derive (Clone, Debug, Default)] |
466 | pub struct PropertyDeclaration { |
467 | pub property_type: Type, |
468 | pub node: Option<SyntaxNode>, |
469 | /// Tells if getter and setter will be added to expose in the native language API |
470 | pub expose_in_public_api: bool, |
471 | /// Public API property exposed as an alias: it shouldn't be generated but instead forward to the alias. |
472 | pub is_alias: Option<NamedReference>, |
473 | pub visibility: PropertyVisibility, |
474 | /// For function or callback: whether it is declared as `pure` (None for private function for which this has to be deduced) |
475 | pub pure: Option<bool>, |
476 | } |
477 | |
478 | impl PropertyDeclaration { |
479 | // For diagnostics: return a node pointing to the type |
480 | pub fn type_node(&self) -> Option<SyntaxNode> { |
481 | let node: &SyntaxNode = self.node.as_ref()?; |
482 | if let Some(x: PropertyDeclaration) = syntax_nodes::PropertyDeclaration::new(node.clone()) { |
483 | Some(x.Type().map_or_else(|| x.into(), |x: Type| x.into())) |
484 | } else { |
485 | node.clone().into() |
486 | } |
487 | } |
488 | } |
489 | |
490 | impl From<Type> for PropertyDeclaration { |
491 | fn from(ty: Type) -> Self { |
492 | PropertyDeclaration { property_type: ty, ..Self::default() } |
493 | } |
494 | } |
495 | |
496 | #[derive (Debug, Clone)] |
497 | pub struct TransitionPropertyAnimation { |
498 | /// The state id as computed in lower_state |
499 | pub state_id: i32, |
500 | /// false for 'to', true for 'out' |
501 | pub is_out: bool, |
502 | /// The content of the `animation` object |
503 | pub animation: ElementRc, |
504 | } |
505 | |
506 | impl TransitionPropertyAnimation { |
507 | /// Return an expression which returns a boolean which is true if the transition is active. |
508 | /// The state argument is an expression referencing the state property of type StateInfo |
509 | pub fn condition(&self, state: Expression) -> Expression { |
510 | Expression::BinaryExpression { |
511 | lhs: Box::new(Expression::StructFieldAccess { |
512 | base: Box::new(state), |
513 | name: (if self.is_out { "previous-state" } else { "current-state" }).into(), |
514 | }), |
515 | rhs: Box::new(Expression::NumberLiteral(self.state_id as _, Unit::None)), |
516 | op: '=' , |
517 | } |
518 | } |
519 | } |
520 | |
521 | #[derive (Debug)] |
522 | pub enum PropertyAnimation { |
523 | Static(ElementRc), |
524 | Transition { state_ref: Expression, animations: Vec<TransitionPropertyAnimation> }, |
525 | } |
526 | |
527 | impl Clone for PropertyAnimation { |
528 | fn clone(&self) -> Self { |
529 | fn deep_clone(e: &ElementRc) -> ElementRc { |
530 | let e = e.borrow(); |
531 | debug_assert!(e.children.is_empty()); |
532 | debug_assert!(e.property_declarations.is_empty()); |
533 | debug_assert!(e.states.is_empty() && e.transitions.is_empty()); |
534 | Rc::new(RefCell::new(Element { |
535 | id: e.id.clone(), |
536 | base_type: e.base_type.clone(), |
537 | bindings: e.bindings.clone(), |
538 | property_analysis: e.property_analysis.clone(), |
539 | enclosing_component: e.enclosing_component.clone(), |
540 | repeated: None, |
541 | debug: e.debug.clone(), |
542 | ..Default::default() |
543 | })) |
544 | } |
545 | match self { |
546 | PropertyAnimation::Static(e) => PropertyAnimation::Static(deep_clone(e)), |
547 | PropertyAnimation::Transition { state_ref, animations } => { |
548 | PropertyAnimation::Transition { |
549 | state_ref: state_ref.clone(), |
550 | animations: animations |
551 | .iter() |
552 | .map(|t| TransitionPropertyAnimation { |
553 | state_id: t.state_id, |
554 | is_out: t.is_out, |
555 | animation: deep_clone(&t.animation), |
556 | }) |
557 | .collect(), |
558 | } |
559 | } |
560 | } |
561 | } |
562 | } |
563 | |
564 | /// Map the accessibility property (eg "accessible-role", "accessible-label") to its named reference |
565 | #[derive (Default, Clone)] |
566 | pub struct AccessibilityProps(pub BTreeMap<String, NamedReference>); |
567 | |
568 | #[derive (Clone, Debug)] |
569 | pub struct GeometryProps { |
570 | pub x: NamedReference, |
571 | pub y: NamedReference, |
572 | pub width: NamedReference, |
573 | pub height: NamedReference, |
574 | } |
575 | |
576 | impl GeometryProps { |
577 | pub fn new(element: &ElementRc) -> Self { |
578 | Self { |
579 | x: NamedReference::new(element, name:"x" ), |
580 | y: NamedReference::new(element, name:"y" ), |
581 | width: NamedReference::new(element, name:"width" ), |
582 | height: NamedReference::new(element, name:"height" ), |
583 | } |
584 | } |
585 | } |
586 | |
587 | pub type BindingsMap = BTreeMap<String, RefCell<BindingExpression>>; |
588 | |
589 | /// An Element is an instantiation of a Component |
590 | #[derive (Default)] |
591 | pub struct Element { |
592 | /// The id as named in the original .slint file. |
593 | /// |
594 | /// Note that it can only be used for lookup before inlining. |
595 | /// After inlining there can be duplicated id in the component. |
596 | /// The id are then re-assigned unique id in the assign_id pass |
597 | pub id: String, |
598 | //pub base: QualifiedTypeName, |
599 | pub base_type: ElementType, |
600 | /// Currently contains also the callbacks. FIXME: should that be changed? |
601 | pub bindings: BindingsMap, |
602 | pub property_analysis: RefCell<HashMap<String, PropertyAnalysis>>, |
603 | |
604 | pub children: Vec<ElementRc>, |
605 | /// The component which contains this element. |
606 | pub enclosing_component: Weak<Component>, |
607 | |
608 | pub property_declarations: BTreeMap<String, PropertyDeclaration>, |
609 | |
610 | /// Main owner for a reference to a property. |
611 | pub named_references: crate::namedreference::NamedReferenceContainer, |
612 | |
613 | /// This element is part of a `for <xxx> in <model>`: |
614 | pub repeated: Option<RepeatedElementInfo>, |
615 | /// This element is a placeholder to embed an Component at |
616 | pub is_component_placeholder: bool, |
617 | |
618 | pub states: Vec<State>, |
619 | pub transitions: Vec<Transition>, |
620 | |
621 | /// true when this item's geometry is handled by a layout |
622 | pub child_of_layout: bool, |
623 | /// The property pointing to the layout info. `(horizontal, vertical)` |
624 | pub layout_info_prop: Option<(NamedReference, NamedReference)>, |
625 | /// Whether we have `preferred-{width,height}: 100%` |
626 | pub default_fill_parent: (bool, bool), |
627 | |
628 | pub accessibility_props: AccessibilityProps, |
629 | |
630 | /// Reference to the property. |
631 | /// This is always initialized from the element constructor, but is Option because it references itself |
632 | pub geometry_props: Option<GeometryProps>, |
633 | |
634 | /// true if this Element is the fake Flickable viewport |
635 | pub is_flickable_viewport: bool, |
636 | |
637 | /// true if this Element may have a popup as child meaning it cannot be optimized |
638 | /// because the popup references it. |
639 | pub has_popup_child: bool, |
640 | |
641 | /// This is the component-local index of this item in the item tree array. |
642 | /// It is generated after the last pass and before the generators run. |
643 | pub item_index: OnceCell<u32>, |
644 | /// the index of the first children in the tree, set with item_index |
645 | pub item_index_of_first_children: OnceCell<u32>, |
646 | |
647 | /// True when this element is in a component was declared with the `:=` symbol instead of the `component` keyword |
648 | pub is_legacy_syntax: bool, |
649 | |
650 | /// How many times the element was inlined |
651 | pub inline_depth: i32, |
652 | |
653 | /// Debug information about this element. |
654 | /// |
655 | /// Contains the AST node if available, as well as wether this element was a layout that had |
656 | /// been lowered into a rectangle in the lower_layouts pass. |
657 | /// There can be several in case of inlining or optimization (child merged into their parent). |
658 | /// |
659 | /// The order in the list is first the parent, and then the removed children. |
660 | pub debug: Vec<(syntax_nodes::Element, Option<crate::layout::Layout>)>, |
661 | } |
662 | |
663 | impl Spanned for Element { |
664 | fn span(&self) -> crate::diagnostics::Span { |
665 | self.debug.first().map(|n: &(Element, Option)| n.0.span()).unwrap_or_default() |
666 | } |
667 | |
668 | fn source_file(&self) -> Option<&crate::diagnostics::SourceFile> { |
669 | self.debug.first().map(|n: &(Element, Option)| &n.0.source_file) |
670 | } |
671 | } |
672 | |
673 | impl core::fmt::Debug for Element { |
674 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
675 | pretty_print(f, self, indentation:0) |
676 | } |
677 | } |
678 | |
679 | pub fn pretty_print( |
680 | f: &mut impl std::fmt::Write, |
681 | e: &Element, |
682 | indentation: usize, |
683 | ) -> std::fmt::Result { |
684 | if let Some(repeated) = &e.repeated { |
685 | write!(f, "for {}[ {}] in " , repeated.model_data_id, repeated.index_id)?; |
686 | expression_tree::pretty_print(f, &repeated.model)?; |
687 | write!(f, ":" )?; |
688 | if let ElementType::Component(base) = &e.base_type { |
689 | if base.parent_element.upgrade().is_some() { |
690 | pretty_print(f, &base.root_element.borrow(), indentation)?; |
691 | return Ok(()); |
692 | } |
693 | } |
694 | } |
695 | if e.is_component_placeholder { |
696 | write!(f, "/* Component Placeholder */ " )?; |
697 | } |
698 | writeln!(f, " {} := {} {{" , e.id, e.base_type)?; |
699 | let mut indentation = indentation + 1; |
700 | macro_rules! indent { |
701 | () => { |
702 | for _ in 0..indentation { |
703 | write!(f, " " )? |
704 | } |
705 | }; |
706 | } |
707 | for (name, ty) in &e.property_declarations { |
708 | indent!(); |
709 | if let Some(alias) = &ty.is_alias { |
710 | writeln!(f, "alias< {}> {} <=> {:?};" , ty.property_type, name, alias)? |
711 | } else { |
712 | writeln!(f, "property< {}> {};" , ty.property_type, name)? |
713 | } |
714 | } |
715 | for (name, expr) in &e.bindings { |
716 | let expr = expr.borrow(); |
717 | indent!(); |
718 | write!(f, " {}: " , name)?; |
719 | expression_tree::pretty_print(f, &expr.expression)?; |
720 | if expr.analysis.as_ref().map_or(false, |a| a.is_const) { |
721 | write!(f, "/*const*/" )?; |
722 | } |
723 | writeln!(f, ";" )?; |
724 | //writeln!(f, "; /*{}*/", expr.priority)?; |
725 | if let Some(anim) = &expr.animation { |
726 | indent!(); |
727 | writeln!(f, "animate {} {:?}" , name, anim)?; |
728 | } |
729 | for nr in &expr.two_way_bindings { |
730 | indent!(); |
731 | writeln!(f, " {} <=> {:?};" , name, nr)?; |
732 | } |
733 | } |
734 | if !e.states.is_empty() { |
735 | indent!(); |
736 | writeln!(f, "states {:?}" , e.states)?; |
737 | } |
738 | if !e.transitions.is_empty() { |
739 | indent!(); |
740 | writeln!(f, "transitions {:?} " , e.transitions)?; |
741 | } |
742 | for c in &e.children { |
743 | indent!(); |
744 | pretty_print(f, &c.borrow(), indentation)? |
745 | } |
746 | for g in &e.geometry_props { |
747 | indent!(); |
748 | writeln!(f, "geometry {:?} " , g)?; |
749 | } |
750 | |
751 | /*if let Type::Component(base) = &e.base_type { |
752 | pretty_print(f, &c.borrow(), indentation)? |
753 | }*/ |
754 | indentation -= 1; |
755 | indent!(); |
756 | writeln!(f, " }}" ) |
757 | } |
758 | |
759 | #[derive (Clone, Default, Debug)] |
760 | pub struct PropertyAnalysis { |
761 | /// true if somewhere in the code, there is an expression that changes this property with an assignment |
762 | pub is_set: bool, |
763 | |
764 | /// True if this property might be set from a different component. |
765 | pub is_set_externally: bool, |
766 | |
767 | /// true if somewhere in the code, an expression is reading this property |
768 | /// Note: currently this is only set in the binding analysis pass |
769 | pub is_read: bool, |
770 | |
771 | /// true if this property is read from another component |
772 | pub is_read_externally: bool, |
773 | |
774 | /// True if the property is linked to another property that is read only. That property becomes read-only |
775 | pub is_linked_to_read_only: bool, |
776 | |
777 | /// True if this property is linked to another property |
778 | pub is_linked: bool, |
779 | } |
780 | |
781 | impl PropertyAnalysis { |
782 | /// Merge analysis from base element for inlining |
783 | /// |
784 | /// Contrary to `merge`, we don't keep the external uses because |
785 | /// they should come from us |
786 | pub fn merge_with_base(&mut self, other: &PropertyAnalysis) { |
787 | self.is_set |= other.is_set; |
788 | self.is_read |= other.is_read; |
789 | } |
790 | |
791 | /// Merge the analysis |
792 | pub fn merge(&mut self, other: &PropertyAnalysis) { |
793 | self.is_set |= other.is_set; |
794 | self.is_read |= other.is_read; |
795 | self.is_read_externally |= other.is_read_externally; |
796 | self.is_set_externally |= other.is_set_externally; |
797 | } |
798 | |
799 | /// Return true if it is read or set or used in any way |
800 | pub fn is_used(&self) -> bool { |
801 | self.is_read || self.is_read_externally || self.is_set || self.is_set_externally |
802 | } |
803 | } |
804 | |
805 | #[derive (Debug, Clone)] |
806 | pub struct ListViewInfo { |
807 | pub viewport_y: NamedReference, |
808 | pub viewport_height: NamedReference, |
809 | pub viewport_width: NamedReference, |
810 | /// The ListView's inner visible height (not counting eventual scrollbar) |
811 | pub listview_height: NamedReference, |
812 | /// The ListView's inner visible width (not counting eventual scrollbar) |
813 | pub listview_width: NamedReference, |
814 | } |
815 | |
816 | #[derive (Debug, Clone)] |
817 | /// If the parent element is a repeated element, this has information about the models |
818 | pub struct RepeatedElementInfo { |
819 | pub model: Expression, |
820 | pub model_data_id: String, |
821 | pub index_id: String, |
822 | /// A conditional element is just a for whose model is a boolean expression |
823 | /// |
824 | /// When this is true, the model is of type boolean instead of Model |
825 | pub is_conditional_element: bool, |
826 | /// When the for is the delegate of a ListView |
827 | pub is_listview: Option<ListViewInfo>, |
828 | } |
829 | |
830 | pub type ElementRc = Rc<RefCell<Element>>; |
831 | pub type ElementWeak = Weak<RefCell<Element>>; |
832 | |
833 | impl Element { |
834 | pub fn make_rc(self) -> ElementRc { |
835 | let r = ElementRc::new(RefCell::new(self)); |
836 | let g = GeometryProps::new(&r); |
837 | r.borrow_mut().geometry_props = Some(g); |
838 | r |
839 | } |
840 | |
841 | pub fn from_node( |
842 | node: syntax_nodes::Element, |
843 | id: String, |
844 | parent_type: ElementType, |
845 | component_child_insertion_point: &mut Option<ChildrenInsertionPoint>, |
846 | is_legacy_syntax: bool, |
847 | diag: &mut BuildDiagnostics, |
848 | tr: &TypeRegister, |
849 | ) -> ElementRc { |
850 | let base_type = if let Some(base_node) = node.QualifiedName() { |
851 | let base = QualifiedTypeName::from_node(base_node.clone()); |
852 | let base_string = base.to_string(); |
853 | match parent_type.lookup_type_for_child_element(&base_string, tr) { |
854 | Ok(ElementType::Component(c)) if c.is_global() => { |
855 | diag.push_error( |
856 | "Cannot create an instance of a global component" .into(), |
857 | &base_node, |
858 | ); |
859 | ElementType::Error |
860 | } |
861 | Ok(ty) => ty, |
862 | Err(err) => { |
863 | diag.push_error(err, &base_node); |
864 | ElementType::Error |
865 | } |
866 | } |
867 | } else if parent_type == ElementType::Global { |
868 | // This must be a global component it can only have properties and callback |
869 | let mut error_on = |node: &dyn Spanned, what: &str| { |
870 | diag.push_error(format!("A global component cannot have {}" , what), node); |
871 | }; |
872 | node.SubElement().for_each(|n| error_on(&n, "sub elements" )); |
873 | node.RepeatedElement().for_each(|n| error_on(&n, "sub elements" )); |
874 | if let Some(n) = node.ChildrenPlaceholder() { |
875 | error_on(&n, "sub elements" ); |
876 | } |
877 | node.PropertyAnimation().for_each(|n| error_on(&n, "animations" )); |
878 | node.States().for_each(|n| error_on(&n, "states" )); |
879 | node.Transitions().for_each(|n| error_on(&n, "transitions" )); |
880 | node.CallbackDeclaration().for_each(|cb| { |
881 | if parser::identifier_text(&cb.DeclaredIdentifier()).map_or(false, |s| s == "init" ) |
882 | { |
883 | error_on(&cb, "an 'init' callback" ) |
884 | } |
885 | }); |
886 | node.CallbackConnection().for_each(|cb| { |
887 | if parser::identifier_text(&cb).map_or(false, |s| s == "init" ) { |
888 | error_on(&cb, "an 'init' callback" ) |
889 | } |
890 | }); |
891 | |
892 | ElementType::Global |
893 | } else if parent_type != ElementType::Error { |
894 | // This should normally never happen because the parser does not allow for this |
895 | assert!(diag.has_error()); |
896 | return ElementRc::default(); |
897 | } else { |
898 | tr.empty_type() |
899 | }; |
900 | let mut r = Element { |
901 | id, |
902 | base_type, |
903 | debug: vec![(node.clone(), None)], |
904 | is_legacy_syntax, |
905 | ..Default::default() |
906 | }; |
907 | |
908 | for prop_decl in node.PropertyDeclaration() { |
909 | let prop_type = prop_decl |
910 | .Type() |
911 | .map(|type_node| type_from_node(type_node, diag, tr)) |
912 | // Type::Void is used for two way bindings without type specified |
913 | .unwrap_or(Type::InferredProperty); |
914 | |
915 | let unresolved_prop_name = |
916 | unwrap_or_continue!(parser::identifier_text(&prop_decl.DeclaredIdentifier()); diag); |
917 | let PropertyLookupResult { |
918 | resolved_name: prop_name, |
919 | property_type: maybe_existing_prop_type, |
920 | .. |
921 | } = r.lookup_property(&unresolved_prop_name); |
922 | match maybe_existing_prop_type { |
923 | Type::Callback { .. } => { |
924 | diag.push_error( |
925 | format!("Cannot declare property ' {}' when a callback with the same name exists" , prop_name), |
926 | &prop_decl.DeclaredIdentifier().child_token(SyntaxKind::Identifier).unwrap(), |
927 | ); |
928 | continue; |
929 | } |
930 | Type::Function { .. } => { |
931 | diag.push_error( |
932 | format!("Cannot declare property ' {}' when a callback with the same name exists" , prop_name), |
933 | &prop_decl.DeclaredIdentifier().child_token(SyntaxKind::Identifier).unwrap(), |
934 | ); |
935 | continue; |
936 | } |
937 | Type::Invalid => {} // Ok to proceed with a new declaration |
938 | _ => { |
939 | diag.push_error( |
940 | format!("Cannot override property ' {}'" , prop_name), |
941 | &prop_decl |
942 | .DeclaredIdentifier() |
943 | .child_token(SyntaxKind::Identifier) |
944 | .unwrap(), |
945 | ); |
946 | continue; |
947 | } |
948 | } |
949 | |
950 | let mut visibility = None; |
951 | for token in prop_decl.children_with_tokens() { |
952 | if token.kind() != SyntaxKind::Identifier { |
953 | continue; |
954 | } |
955 | match (token.as_token().unwrap().text(), visibility) { |
956 | ("in" , None) => visibility = Some(PropertyVisibility::Input), |
957 | ("in" , Some(_)) => diag.push_error("Extra 'in' keyword" .into(), &token), |
958 | ("out" , None) => visibility = Some(PropertyVisibility::Output), |
959 | ("out" , Some(_)) => diag.push_error("Extra 'out' keyword" .into(), &token), |
960 | ("in-out" | "in_out" , None) => visibility = Some(PropertyVisibility::InOut), |
961 | ("in-out" | "in_out" , Some(_)) => { |
962 | diag.push_error("Extra 'in-out' keyword" .into(), &token) |
963 | } |
964 | ("private" , None) => visibility = Some(PropertyVisibility::Private), |
965 | ("private" , Some(_)) => { |
966 | diag.push_error("Extra 'private' keyword" .into(), &token) |
967 | } |
968 | _ => (), |
969 | } |
970 | } |
971 | let visibility = visibility.unwrap_or({ |
972 | if is_legacy_syntax { |
973 | PropertyVisibility::InOut |
974 | } else { |
975 | PropertyVisibility::Private |
976 | } |
977 | }); |
978 | |
979 | r.property_declarations.insert( |
980 | prop_name.to_string(), |
981 | PropertyDeclaration { |
982 | property_type: prop_type, |
983 | node: Some(prop_decl.clone().into()), |
984 | visibility, |
985 | ..Default::default() |
986 | }, |
987 | ); |
988 | |
989 | if let Some(csn) = prop_decl.BindingExpression() { |
990 | match r.bindings.entry(prop_name.to_string()) { |
991 | Entry::Vacant(e) => { |
992 | e.insert(BindingExpression::new_uncompiled(csn.into()).into()); |
993 | } |
994 | Entry::Occupied(_) => { |
995 | diag.push_error( |
996 | "Duplicated property binding" .into(), |
997 | &prop_decl.DeclaredIdentifier(), |
998 | ); |
999 | } |
1000 | } |
1001 | } |
1002 | if let Some(csn) = prop_decl.TwoWayBinding() { |
1003 | if r.bindings |
1004 | .insert(prop_name.into(), BindingExpression::new_uncompiled(csn.into()).into()) |
1005 | .is_some() |
1006 | { |
1007 | diag.push_error( |
1008 | "Duplicated property binding" .into(), |
1009 | &prop_decl.DeclaredIdentifier(), |
1010 | ); |
1011 | } |
1012 | } |
1013 | } |
1014 | |
1015 | r.parse_bindings( |
1016 | node.Binding().filter_map(|b| { |
1017 | Some((b.child_token(SyntaxKind::Identifier)?, b.BindingExpression().into())) |
1018 | }), |
1019 | is_legacy_syntax, |
1020 | diag, |
1021 | ); |
1022 | r.parse_bindings( |
1023 | node.TwoWayBinding() |
1024 | .filter_map(|b| Some((b.child_token(SyntaxKind::Identifier)?, b.into()))), |
1025 | is_legacy_syntax, |
1026 | diag, |
1027 | ); |
1028 | |
1029 | apply_default_type_properties(&mut r); |
1030 | |
1031 | for sig_decl in node.CallbackDeclaration() { |
1032 | let name = |
1033 | unwrap_or_continue!(parser::identifier_text(&sig_decl.DeclaredIdentifier()); diag); |
1034 | |
1035 | let pure = Some( |
1036 | sig_decl.child_token(SyntaxKind::Identifier).map_or(false, |t| t.text() == "pure" ), |
1037 | ); |
1038 | |
1039 | if let Some(csn) = sig_decl.TwoWayBinding() { |
1040 | r.bindings |
1041 | .insert(name.clone(), BindingExpression::new_uncompiled(csn.into()).into()); |
1042 | r.property_declarations.insert( |
1043 | name, |
1044 | PropertyDeclaration { |
1045 | property_type: Type::InferredCallback, |
1046 | node: Some(sig_decl.into()), |
1047 | visibility: PropertyVisibility::InOut, |
1048 | pure, |
1049 | ..Default::default() |
1050 | }, |
1051 | ); |
1052 | continue; |
1053 | } |
1054 | |
1055 | let PropertyLookupResult { |
1056 | resolved_name: existing_name, |
1057 | property_type: maybe_existing_prop_type, |
1058 | .. |
1059 | } = r.lookup_property(&name); |
1060 | if !matches!(maybe_existing_prop_type, Type::Invalid) { |
1061 | if matches!(maybe_existing_prop_type, Type::Callback { .. }) { |
1062 | if r.property_declarations.contains_key(&name) { |
1063 | diag.push_error( |
1064 | "Duplicated callback declaration" .into(), |
1065 | &sig_decl.DeclaredIdentifier(), |
1066 | ); |
1067 | } else { |
1068 | diag.push_error( |
1069 | format!("Cannot override callback ' {}'" , existing_name), |
1070 | &sig_decl.DeclaredIdentifier(), |
1071 | ) |
1072 | } |
1073 | } else { |
1074 | diag.push_error( |
1075 | format!( |
1076 | "Cannot declare callback ' {existing_name}' when a {} with the same name exists" , |
1077 | if matches!(maybe_existing_prop_type, Type::Function { .. }) { "function" } else { "property" } |
1078 | ), |
1079 | &sig_decl.DeclaredIdentifier(), |
1080 | ); |
1081 | } |
1082 | continue; |
1083 | } |
1084 | |
1085 | let args = sig_decl.Type().map(|node_ty| type_from_node(node_ty, diag, tr)).collect(); |
1086 | let return_type = sig_decl |
1087 | .ReturnType() |
1088 | .map(|ret_ty| Box::new(type_from_node(ret_ty.Type(), diag, tr))); |
1089 | r.property_declarations.insert( |
1090 | name, |
1091 | PropertyDeclaration { |
1092 | property_type: Type::Callback { return_type, args }, |
1093 | node: Some(sig_decl.into()), |
1094 | visibility: PropertyVisibility::InOut, |
1095 | pure, |
1096 | ..Default::default() |
1097 | }, |
1098 | ); |
1099 | } |
1100 | |
1101 | for func in node.Function() { |
1102 | let name = |
1103 | unwrap_or_continue!(parser::identifier_text(&func.DeclaredIdentifier()); diag); |
1104 | |
1105 | let PropertyLookupResult { |
1106 | resolved_name: existing_name, |
1107 | property_type: maybe_existing_prop_type, |
1108 | .. |
1109 | } = r.lookup_property(&name); |
1110 | if !matches!(maybe_existing_prop_type, Type::Invalid) { |
1111 | if matches!(maybe_existing_prop_type, Type::Callback { .. } | Type::Function { .. }) |
1112 | { |
1113 | diag.push_error( |
1114 | format!("Cannot override ' {}'" , existing_name), |
1115 | &func.DeclaredIdentifier(), |
1116 | ) |
1117 | } else { |
1118 | diag.push_error( |
1119 | format!("Cannot declare function ' {}' when a property with the same name exists" , existing_name), |
1120 | &func.DeclaredIdentifier(), |
1121 | ); |
1122 | } |
1123 | continue; |
1124 | } |
1125 | |
1126 | let mut args = vec![]; |
1127 | let mut arg_names = vec![]; |
1128 | for a in func.ArgumentDeclaration() { |
1129 | args.push(type_from_node(a.Type(), diag, tr)); |
1130 | let name = |
1131 | unwrap_or_continue!(parser::identifier_text(&a.DeclaredIdentifier()); diag); |
1132 | if arg_names.contains(&name) { |
1133 | diag.push_error( |
1134 | format!("Duplicated argument name ' {name}'" ), |
1135 | &a.DeclaredIdentifier(), |
1136 | ); |
1137 | } |
1138 | arg_names.push(name); |
1139 | } |
1140 | let return_type = Box::new( |
1141 | func.ReturnType() |
1142 | .map_or(Type::Void, |ret_ty| type_from_node(ret_ty.Type(), diag, tr)), |
1143 | ); |
1144 | if r.bindings |
1145 | .insert(name.clone(), BindingExpression::new_uncompiled(func.clone().into()).into()) |
1146 | .is_some() |
1147 | { |
1148 | assert!(diag.has_error()); |
1149 | } |
1150 | |
1151 | let mut visibility = PropertyVisibility::Private; |
1152 | let mut pure = None; |
1153 | for token in func.children_with_tokens() { |
1154 | if token.kind() != SyntaxKind::Identifier { |
1155 | continue; |
1156 | } |
1157 | match token.as_token().unwrap().text() { |
1158 | "pure" => pure = Some(true), |
1159 | "public" => { |
1160 | debug_assert_eq!(visibility, PropertyVisibility::Private); |
1161 | visibility = PropertyVisibility::Public; |
1162 | pure = pure.or(Some(false)); |
1163 | } |
1164 | "protected" => { |
1165 | debug_assert_eq!(visibility, PropertyVisibility::Private); |
1166 | visibility = PropertyVisibility::Protected; |
1167 | pure = pure.or(Some(false)); |
1168 | } |
1169 | _ => (), |
1170 | } |
1171 | } |
1172 | |
1173 | r.property_declarations.insert( |
1174 | name, |
1175 | PropertyDeclaration { |
1176 | property_type: Type::Function { return_type, args }, |
1177 | node: Some(func.into()), |
1178 | visibility, |
1179 | pure, |
1180 | ..Default::default() |
1181 | }, |
1182 | ); |
1183 | } |
1184 | |
1185 | for con_node in node.CallbackConnection() { |
1186 | let unresolved_name = unwrap_or_continue!(parser::identifier_text(&con_node); diag); |
1187 | let PropertyLookupResult { resolved_name, property_type, .. } = |
1188 | r.lookup_property(&unresolved_name); |
1189 | if let Type::Callback { args, .. } = &property_type { |
1190 | let num_arg = con_node.DeclaredIdentifier().count(); |
1191 | if num_arg > args.len() { |
1192 | diag.push_error( |
1193 | format!( |
1194 | "' {}' only has {} arguments, but {} were provided" , |
1195 | unresolved_name, |
1196 | args.len(), |
1197 | num_arg |
1198 | ), |
1199 | &con_node.child_token(SyntaxKind::Identifier).unwrap(), |
1200 | ); |
1201 | } |
1202 | } else if property_type == Type::InferredCallback { |
1203 | // argument matching will happen later |
1204 | } else { |
1205 | diag.push_error( |
1206 | format!("' {}' is not a callback in {}" , unresolved_name, r.base_type), |
1207 | &con_node.child_token(SyntaxKind::Identifier).unwrap(), |
1208 | ); |
1209 | continue; |
1210 | } |
1211 | match r.bindings.entry(resolved_name.into_owned()) { |
1212 | Entry::Vacant(e) => { |
1213 | e.insert(BindingExpression::new_uncompiled(con_node.clone().into()).into()); |
1214 | } |
1215 | Entry::Occupied(_) => diag.push_error( |
1216 | "Duplicated callback" .into(), |
1217 | &con_node.child_token(SyntaxKind::Identifier).unwrap(), |
1218 | ), |
1219 | } |
1220 | } |
1221 | |
1222 | for anim in node.PropertyAnimation() { |
1223 | if let Some(star) = anim.child_token(SyntaxKind::Star) { |
1224 | diag.push_error( |
1225 | "catch-all property is only allowed within transitions" .into(), |
1226 | &star, |
1227 | ) |
1228 | }; |
1229 | for prop_name_token in anim.QualifiedName() { |
1230 | match QualifiedTypeName::from_node(prop_name_token.clone()).members.as_slice() { |
1231 | [unresolved_prop_name] => { |
1232 | let lookup_result = r.lookup_property(unresolved_prop_name); |
1233 | let valid_assign = lookup_result.is_valid_for_assignment(); |
1234 | if let Some(anim_element) = animation_element_from_node( |
1235 | &anim, |
1236 | &prop_name_token, |
1237 | lookup_result.property_type, |
1238 | diag, |
1239 | tr, |
1240 | ) { |
1241 | if !valid_assign { |
1242 | diag.push_error( |
1243 | format!( |
1244 | "Cannot animate {} property ' {}'" , |
1245 | lookup_result.property_visibility, unresolved_prop_name |
1246 | ), |
1247 | &prop_name_token, |
1248 | ); |
1249 | } |
1250 | |
1251 | if unresolved_prop_name != lookup_result.resolved_name.as_ref() { |
1252 | diag.push_property_deprecation_warning( |
1253 | unresolved_prop_name, |
1254 | &lookup_result.resolved_name, |
1255 | &prop_name_token, |
1256 | ); |
1257 | } |
1258 | |
1259 | let expr_binding = r |
1260 | .bindings |
1261 | .entry(lookup_result.resolved_name.to_string()) |
1262 | .or_insert_with(|| { |
1263 | let mut r = BindingExpression::from(Expression::Invalid); |
1264 | r.priority = 1; |
1265 | r.span = Some(prop_name_token.to_source_location()); |
1266 | r.into() |
1267 | }); |
1268 | if expr_binding |
1269 | .get_mut() |
1270 | .animation |
1271 | .replace(PropertyAnimation::Static(anim_element)) |
1272 | .is_some() |
1273 | { |
1274 | diag.push_error("Duplicated animation" .into(), &prop_name_token) |
1275 | } |
1276 | } |
1277 | } |
1278 | _ => diag.push_error( |
1279 | "Can only refer to property in the current element" .into(), |
1280 | &prop_name_token, |
1281 | ), |
1282 | } |
1283 | } |
1284 | } |
1285 | |
1286 | let mut children_placeholder = None; |
1287 | let r = r.make_rc(); |
1288 | |
1289 | for se in node.children() { |
1290 | if se.kind() == SyntaxKind::SubElement { |
1291 | let parent_type = r.borrow().base_type.clone(); |
1292 | r.borrow_mut().children.push(Element::from_sub_element_node( |
1293 | se.into(), |
1294 | parent_type, |
1295 | component_child_insertion_point, |
1296 | is_legacy_syntax, |
1297 | diag, |
1298 | tr, |
1299 | )); |
1300 | } else if se.kind() == SyntaxKind::RepeatedElement { |
1301 | let mut sub_child_insertion_point = None; |
1302 | let rep = Element::from_repeated_node( |
1303 | se.into(), |
1304 | &r, |
1305 | &mut sub_child_insertion_point, |
1306 | is_legacy_syntax, |
1307 | diag, |
1308 | tr, |
1309 | ); |
1310 | if let Some((_, se)) = sub_child_insertion_point { |
1311 | diag.push_error( |
1312 | "The @children placeholder cannot appear in a repeated element" .into(), |
1313 | &se, |
1314 | ) |
1315 | } |
1316 | r.borrow_mut().children.push(rep); |
1317 | } else if se.kind() == SyntaxKind::ConditionalElement { |
1318 | let mut sub_child_insertion_point = None; |
1319 | let rep = Element::from_conditional_node( |
1320 | se.into(), |
1321 | r.borrow().base_type.clone(), |
1322 | &mut sub_child_insertion_point, |
1323 | is_legacy_syntax, |
1324 | diag, |
1325 | tr, |
1326 | ); |
1327 | if let Some((_, se)) = sub_child_insertion_point { |
1328 | diag.push_error( |
1329 | "The @children placeholder cannot appear in a conditional element" .into(), |
1330 | &se, |
1331 | ) |
1332 | } |
1333 | r.borrow_mut().children.push(rep); |
1334 | } else if se.kind() == SyntaxKind::ChildrenPlaceholder { |
1335 | if children_placeholder.is_some() { |
1336 | diag.push_error( |
1337 | "The @children placeholder can only appear once in an element" .into(), |
1338 | &se, |
1339 | ) |
1340 | } else { |
1341 | children_placeholder = Some(se.clone().into()); |
1342 | } |
1343 | } |
1344 | } |
1345 | |
1346 | if let Some(children_placeholder) = children_placeholder { |
1347 | if component_child_insertion_point.is_some() { |
1348 | diag.push_error( |
1349 | "The @children placeholder can only appear once in an element hierarchy" .into(), |
1350 | &children_placeholder, |
1351 | ) |
1352 | } else { |
1353 | *component_child_insertion_point = Some((r.clone(), children_placeholder)); |
1354 | } |
1355 | } |
1356 | |
1357 | for state in node.States().flat_map(|s| s.State()) { |
1358 | let s = State { |
1359 | id: parser::identifier_text(&state.DeclaredIdentifier()).unwrap_or_default(), |
1360 | condition: state.Expression().map(|e| Expression::Uncompiled(e.into())), |
1361 | property_changes: state |
1362 | .StatePropertyChange() |
1363 | .filter_map(|s| { |
1364 | lookup_property_from_qualified_name_for_state(s.QualifiedName(), &r, diag) |
1365 | .map(|(ne, _)| { |
1366 | (ne, Expression::Uncompiled(s.BindingExpression().into()), s) |
1367 | }) |
1368 | }) |
1369 | .collect(), |
1370 | }; |
1371 | for trs in state.Transition() { |
1372 | let mut t = Transition::from_node(trs, &r, tr, diag); |
1373 | t.state_id = s.id.clone(); |
1374 | r.borrow_mut().transitions.push(t); |
1375 | } |
1376 | r.borrow_mut().states.push(s); |
1377 | } |
1378 | |
1379 | for ts in node.Transitions() { |
1380 | if !is_legacy_syntax { |
1381 | diag.push_error("'transitions' block are no longer supported. Use 'in {...}' and 'out {...}' directly in the state definition" .into(), &ts); |
1382 | } |
1383 | for trs in ts.Transition() { |
1384 | let trans = Transition::from_node(trs, &r, tr, diag); |
1385 | r.borrow_mut().transitions.push(trans); |
1386 | } |
1387 | } |
1388 | |
1389 | if r.borrow().base_type.to_string() == "ListView" { |
1390 | let mut seen_for = false; |
1391 | for se in node.children() { |
1392 | if se.kind() == SyntaxKind::RepeatedElement && !seen_for { |
1393 | seen_for = true; |
1394 | } else if matches!( |
1395 | se.kind(), |
1396 | SyntaxKind::SubElement |
1397 | | SyntaxKind::ConditionalElement |
1398 | | SyntaxKind::RepeatedElement |
1399 | | SyntaxKind::ChildrenPlaceholder |
1400 | ) { |
1401 | diag.push_error("A ListView can just have a single 'for' as children. Anything else is not supported" .into(), &se) |
1402 | } |
1403 | } |
1404 | } |
1405 | |
1406 | r |
1407 | } |
1408 | |
1409 | fn from_sub_element_node( |
1410 | node: syntax_nodes::SubElement, |
1411 | parent_type: ElementType, |
1412 | component_child_insertion_point: &mut Option<ChildrenInsertionPoint>, |
1413 | is_in_legacy_component: bool, |
1414 | diag: &mut BuildDiagnostics, |
1415 | tr: &TypeRegister, |
1416 | ) -> ElementRc { |
1417 | let mut id = parser::identifier_text(&node).unwrap_or_default(); |
1418 | if matches!(id.as_ref(), "parent" | "self" | "root" ) { |
1419 | diag.push_error( |
1420 | format!("' {}' is a reserved id" , id), |
1421 | &node.child_token(SyntaxKind::Identifier).unwrap(), |
1422 | ); |
1423 | id = String::new(); |
1424 | } |
1425 | Element::from_node( |
1426 | node.Element(), |
1427 | id, |
1428 | parent_type, |
1429 | component_child_insertion_point, |
1430 | is_in_legacy_component, |
1431 | diag, |
1432 | tr, |
1433 | ) |
1434 | } |
1435 | |
1436 | fn from_repeated_node( |
1437 | node: syntax_nodes::RepeatedElement, |
1438 | parent: &ElementRc, |
1439 | component_child_insertion_point: &mut Option<ChildrenInsertionPoint>, |
1440 | is_in_legacy_component: bool, |
1441 | diag: &mut BuildDiagnostics, |
1442 | tr: &TypeRegister, |
1443 | ) -> ElementRc { |
1444 | let is_listview = if parent.borrow().base_type.to_string() == "ListView" { |
1445 | Some(ListViewInfo { |
1446 | viewport_y: NamedReference::new(parent, "viewport-y" ), |
1447 | viewport_height: NamedReference::new(parent, "viewport-height" ), |
1448 | viewport_width: NamedReference::new(parent, "viewport-width" ), |
1449 | listview_height: NamedReference::new(parent, "visible-height" ), |
1450 | listview_width: NamedReference::new(parent, "visible-width" ), |
1451 | }) |
1452 | } else { |
1453 | None |
1454 | }; |
1455 | let rei = RepeatedElementInfo { |
1456 | model: Expression::Uncompiled(node.Expression().into()), |
1457 | model_data_id: node |
1458 | .DeclaredIdentifier() |
1459 | .and_then(|n| parser::identifier_text(&n)) |
1460 | .unwrap_or_default(), |
1461 | index_id: node |
1462 | .RepeatedIndex() |
1463 | .and_then(|r| parser::identifier_text(&r)) |
1464 | .unwrap_or_default(), |
1465 | is_conditional_element: false, |
1466 | is_listview, |
1467 | }; |
1468 | let e = Element::from_sub_element_node( |
1469 | node.SubElement(), |
1470 | parent.borrow().base_type.clone(), |
1471 | component_child_insertion_point, |
1472 | is_in_legacy_component, |
1473 | diag, |
1474 | tr, |
1475 | ); |
1476 | e.borrow_mut().repeated = Some(rei); |
1477 | e |
1478 | } |
1479 | |
1480 | fn from_conditional_node( |
1481 | node: syntax_nodes::ConditionalElement, |
1482 | parent_type: ElementType, |
1483 | component_child_insertion_point: &mut Option<ChildrenInsertionPoint>, |
1484 | is_in_legacy_component: bool, |
1485 | diag: &mut BuildDiagnostics, |
1486 | tr: &TypeRegister, |
1487 | ) -> ElementRc { |
1488 | let rei = RepeatedElementInfo { |
1489 | model: Expression::Uncompiled(node.Expression().into()), |
1490 | model_data_id: String::new(), |
1491 | index_id: String::new(), |
1492 | is_conditional_element: true, |
1493 | is_listview: None, |
1494 | }; |
1495 | let e = Element::from_sub_element_node( |
1496 | node.SubElement(), |
1497 | parent_type, |
1498 | component_child_insertion_point, |
1499 | is_in_legacy_component, |
1500 | diag, |
1501 | tr, |
1502 | ); |
1503 | e.borrow_mut().repeated = Some(rei); |
1504 | e |
1505 | } |
1506 | |
1507 | /// Return the type of a property in this element or its base, along with the final name, in case |
1508 | /// the provided name points towards a property alias. Type::Invalid is returned if the property does |
1509 | /// not exist. |
1510 | pub fn lookup_property<'a>(&self, name: &'a str) -> PropertyLookupResult<'a> { |
1511 | self.property_declarations.get(name).map_or_else( |
1512 | || { |
1513 | let mut r = self.base_type.lookup_property(name); |
1514 | r.is_in_direct_base = r.is_local_to_component; |
1515 | r.is_local_to_component = false; |
1516 | r |
1517 | }, |
1518 | |p| PropertyLookupResult { |
1519 | resolved_name: name.into(), |
1520 | property_type: p.property_type.clone(), |
1521 | property_visibility: p.visibility, |
1522 | declared_pure: p.pure, |
1523 | is_local_to_component: true, |
1524 | is_in_direct_base: false, |
1525 | }, |
1526 | ) |
1527 | } |
1528 | |
1529 | fn parse_bindings( |
1530 | &mut self, |
1531 | bindings: impl Iterator<Item = (crate::parser::SyntaxToken, SyntaxNode)>, |
1532 | is_in_legacy_component: bool, |
1533 | diag: &mut BuildDiagnostics, |
1534 | ) { |
1535 | for (name_token, b) in bindings { |
1536 | let unresolved_name = crate::parser::normalize_identifier(name_token.text()); |
1537 | let lookup_result = self.lookup_property(&unresolved_name); |
1538 | if !lookup_result.property_type.is_property_type() { |
1539 | match lookup_result.property_type { |
1540 | Type::Invalid => { |
1541 | if self.base_type != ElementType::Error { |
1542 | diag.push_error(if self.base_type.to_string() == "Empty" { |
1543 | format!( "Unknown property {unresolved_name}" ) |
1544 | } else { |
1545 | format!( "Unknown property {unresolved_name} in {}" , self.base_type) |
1546 | }, |
1547 | &name_token); |
1548 | } |
1549 | } |
1550 | Type::Callback { .. } => { |
1551 | diag.push_error(format!("' {}' is a callback. Use `=>` to connect" , unresolved_name), |
1552 | &name_token) |
1553 | } |
1554 | _ => diag.push_error(format!( |
1555 | "Cannot assign to {} in {} because it does not have a valid property type" , |
1556 | unresolved_name, self.base_type, |
1557 | ), |
1558 | &name_token), |
1559 | } |
1560 | } else if !lookup_result.is_local_to_component |
1561 | && (lookup_result.property_visibility == PropertyVisibility::Private |
1562 | || lookup_result.property_visibility == PropertyVisibility::Output) |
1563 | { |
1564 | if is_in_legacy_component |
1565 | && lookup_result.property_visibility == PropertyVisibility::Output |
1566 | { |
1567 | diag.push_warning( |
1568 | format!("Assigning to output property ' {unresolved_name}' is deprecated" ), |
1569 | &name_token, |
1570 | ); |
1571 | } else { |
1572 | diag.push_error( |
1573 | format!( |
1574 | "Cannot assign to {} property ' {}'" , |
1575 | lookup_result.property_visibility, unresolved_name |
1576 | ), |
1577 | &name_token, |
1578 | ); |
1579 | } |
1580 | } |
1581 | |
1582 | if lookup_result.resolved_name != unresolved_name { |
1583 | diag.push_property_deprecation_warning( |
1584 | &unresolved_name, |
1585 | &lookup_result.resolved_name, |
1586 | &name_token, |
1587 | ); |
1588 | } |
1589 | |
1590 | match self.bindings.entry(lookup_result.resolved_name.to_string()) { |
1591 | Entry::Occupied(_) => { |
1592 | diag.push_error("Duplicated property binding" .into(), &name_token); |
1593 | } |
1594 | Entry::Vacant(entry) => { |
1595 | entry.insert(BindingExpression::new_uncompiled(b).into()); |
1596 | } |
1597 | }; |
1598 | } |
1599 | } |
1600 | |
1601 | pub fn native_class(&self) -> Option<Rc<NativeClass>> { |
1602 | let mut base_type = self.base_type.clone(); |
1603 | loop { |
1604 | match &base_type { |
1605 | ElementType::Component(component) => { |
1606 | base_type = component.root_element.clone().borrow().base_type.clone(); |
1607 | } |
1608 | ElementType::Builtin(builtin) => break Some(builtin.native_class.clone()), |
1609 | ElementType::Native(native) => break Some(native.clone()), |
1610 | _ => break None, |
1611 | } |
1612 | } |
1613 | } |
1614 | |
1615 | pub fn builtin_type(&self) -> Option<Rc<BuiltinElement>> { |
1616 | let mut base_type = self.base_type.clone(); |
1617 | loop { |
1618 | match &base_type { |
1619 | ElementType::Component(component) => { |
1620 | base_type = component.root_element.clone().borrow().base_type.clone(); |
1621 | } |
1622 | ElementType::Builtin(builtin) => break Some(builtin.clone()), |
1623 | _ => break None, |
1624 | } |
1625 | } |
1626 | } |
1627 | |
1628 | pub fn layout_info_prop(&self, orientation: Orientation) -> Option<&NamedReference> { |
1629 | self.layout_info_prop.as_ref().map(|prop| match orientation { |
1630 | Orientation::Horizontal => &prop.0, |
1631 | Orientation::Vertical => &prop.1, |
1632 | }) |
1633 | } |
1634 | |
1635 | /// Returns the element's name as specified in the markup, not normalized. |
1636 | pub fn original_name(&self) -> String { |
1637 | self.debug |
1638 | .first() |
1639 | .and_then(|n| n.0.child_token(parser::SyntaxKind::Identifier)) |
1640 | .map(|n| n.to_string()) |
1641 | .unwrap_or_else(|| self.id.clone()) |
1642 | } |
1643 | |
1644 | /// Return true if the binding is set, either on this element or in a base |
1645 | /// |
1646 | /// If `need_explicit` is true, then only consider binding set in the code, not the ones set |
1647 | /// by the compiler later. |
1648 | pub fn is_binding_set(self: &Element, property_name: &str, need_explicit: bool) -> bool { |
1649 | if self.bindings.get(property_name).map_or(false, |b| { |
1650 | b.borrow().has_binding() && (!need_explicit || b.borrow().priority > 0) |
1651 | }) { |
1652 | true |
1653 | } else if let ElementType::Component(base) = &self.base_type { |
1654 | base.root_element.borrow().is_binding_set(property_name, need_explicit) |
1655 | } else { |
1656 | false |
1657 | } |
1658 | } |
1659 | |
1660 | /// Set the property `property_name` of this Element only if it was not set. |
1661 | /// the `expression_fn` will only be called if it isn't set |
1662 | /// |
1663 | /// returns true if the binding was changed |
1664 | pub fn set_binding_if_not_set( |
1665 | &mut self, |
1666 | property_name: String, |
1667 | expression_fn: impl FnOnce() -> Expression, |
1668 | ) -> bool { |
1669 | if self.is_binding_set(&property_name, false) { |
1670 | return false; |
1671 | } |
1672 | |
1673 | match self.bindings.entry(property_name) { |
1674 | Entry::Vacant(vacant_entry) => { |
1675 | let mut binding: BindingExpression = expression_fn().into(); |
1676 | binding.priority = i32::MAX; |
1677 | vacant_entry.insert(binding.into()); |
1678 | } |
1679 | Entry::Occupied(mut existing_entry) => { |
1680 | let mut binding: BindingExpression = expression_fn().into(); |
1681 | binding.priority = i32::MAX; |
1682 | existing_entry.get_mut().get_mut().merge_with(&binding); |
1683 | } |
1684 | }; |
1685 | true |
1686 | } |
1687 | |
1688 | pub fn sub_component(&self) -> Option<&Rc<Component>> { |
1689 | if self.repeated.is_some() || self.is_component_placeholder { |
1690 | None |
1691 | } else if let ElementType::Component(sub_component) = &self.base_type { |
1692 | Some(sub_component) |
1693 | } else { |
1694 | None |
1695 | } |
1696 | } |
1697 | } |
1698 | |
1699 | /// Apply default property values defined in `builtins.slint` to the element. |
1700 | fn apply_default_type_properties(element: &mut Element) { |
1701 | // Apply default property values on top: |
1702 | if let ElementType::Builtin(builtin_base: &Rc) = &element.base_type { |
1703 | for (prop: &String, info: &BuiltinPropertyInfo) in &builtin_base.properties { |
1704 | if let Some(expr: &Expression) = &info.default_value { |
1705 | element.bindings.entry(prop.clone()).or_insert_with(|| { |
1706 | let mut binding: BindingExpression = BindingExpression::from(expr.clone()); |
1707 | binding.priority = i32::MAX; |
1708 | RefCell::new(binding) |
1709 | }); |
1710 | } |
1711 | } |
1712 | } |
1713 | } |
1714 | |
1715 | /// Create a Type for this node |
1716 | pub fn type_from_node( |
1717 | node: syntax_nodes::Type, |
1718 | diag: &mut BuildDiagnostics, |
1719 | tr: &TypeRegister, |
1720 | ) -> Type { |
1721 | if let Some(qualified_type_node: QualifiedName) = node.QualifiedName() { |
1722 | let qualified_type: QualifiedTypeName = QualifiedTypeName::from_node(qualified_type_node.clone()); |
1723 | |
1724 | let prop_type: Type = tr.lookup_qualified(&qualified_type.members); |
1725 | |
1726 | if prop_type == Type::Invalid && tr.lookup_element(&qualified_type.to_string()).is_err() { |
1727 | diag.push_error(message:format!("Unknown type ' {}'" , qualified_type), &qualified_type_node); |
1728 | } else if !prop_type.is_property_type() { |
1729 | diag.push_error( |
1730 | message:format!("' {}' is not a valid type" , qualified_type), |
1731 | &qualified_type_node, |
1732 | ); |
1733 | } |
1734 | prop_type |
1735 | } else if let Some(object_node: ObjectType) = node.ObjectType() { |
1736 | type_struct_from_node(object_node, diag, tr, rust_attributes:None) |
1737 | } else if let Some(array_node: ArrayType) = node.ArrayType() { |
1738 | Type::Array(Box::new(type_from_node(node:array_node.Type(), diag, tr))) |
1739 | } else { |
1740 | assert!(diag.has_error()); |
1741 | Type::Invalid |
1742 | } |
1743 | } |
1744 | |
1745 | /// Create a [`Type::Struct`] from a [`syntax_nodes::ObjectType`] |
1746 | pub fn type_struct_from_node( |
1747 | object_node: syntax_nodes::ObjectType, |
1748 | diag: &mut BuildDiagnostics, |
1749 | tr: &TypeRegister, |
1750 | rust_attributes: Option<Vec<String>>, |
1751 | ) -> Type { |
1752 | let fields: BTreeMap = object_nodeimpl Iterator |
1753 | .ObjectTypeMember() |
1754 | .map(|member: ObjectTypeMember| { |
1755 | ( |
1756 | parser::identifier_text(&member).unwrap_or_default(), |
1757 | type_from_node(node:member.Type(), diag, tr), |
1758 | ) |
1759 | }) |
1760 | .collect(); |
1761 | Type::Struct { fields, name: None, node: Some(object_node), rust_attributes } |
1762 | } |
1763 | |
1764 | fn animation_element_from_node( |
1765 | anim: &syntax_nodes::PropertyAnimation, |
1766 | prop_name: &syntax_nodes::QualifiedName, |
1767 | prop_type: Type, |
1768 | diag: &mut BuildDiagnostics, |
1769 | tr: &TypeRegister, |
1770 | ) -> Option<ElementRc> { |
1771 | let anim_type = tr.property_animation_type_for_property(prop_type); |
1772 | if !matches!(anim_type, ElementType::Builtin(..)) { |
1773 | diag.push_error( |
1774 | format!( |
1775 | "' {}' is not a property that can be animated" , |
1776 | prop_name.text().to_string().trim() |
1777 | ), |
1778 | prop_name, |
1779 | ); |
1780 | None |
1781 | } else { |
1782 | let mut anim_element = |
1783 | Element { id: "" .into(), base_type: anim_type, ..Default::default() }; |
1784 | anim_element.parse_bindings( |
1785 | anim.Binding().filter_map(|b| { |
1786 | Some((b.child_token(SyntaxKind::Identifier)?, b.BindingExpression().into())) |
1787 | }), |
1788 | false, |
1789 | diag, |
1790 | ); |
1791 | |
1792 | apply_default_type_properties(&mut anim_element); |
1793 | |
1794 | Some(Rc::new(RefCell::new(anim_element))) |
1795 | } |
1796 | } |
1797 | |
1798 | #[derive (Default, Debug, Clone)] |
1799 | pub struct QualifiedTypeName { |
1800 | pub members: Vec<String>, |
1801 | } |
1802 | |
1803 | impl QualifiedTypeName { |
1804 | pub fn from_node(node: syntax_nodes::QualifiedName) -> Self { |
1805 | debug_assert_eq!(node.kind(), SyntaxKind::QualifiedName); |
1806 | let members: Vec = nodeQualifiedName |
1807 | .children_with_tokens() |
1808 | .filter(|n| n.kind() == SyntaxKind::Identifier) |
1809 | .filter_map(|x| x.as_token().map(|x| crate::parser::normalize_identifier(ident:x.text()))) |
1810 | .collect(); |
1811 | Self { members } |
1812 | } |
1813 | } |
1814 | |
1815 | impl Display for QualifiedTypeName { |
1816 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
1817 | write!(f, " {}" , self.members.join("." )) |
1818 | } |
1819 | } |
1820 | |
1821 | /// Return a NamedReference for a qualified name used in a state (or transition), |
1822 | /// if the reference is invalid, there will be a diagnostic |
1823 | fn lookup_property_from_qualified_name_for_state( |
1824 | node: syntax_nodes::QualifiedName, |
1825 | r: &ElementRc, |
1826 | diag: &mut BuildDiagnostics, |
1827 | ) -> Option<(NamedReference, Type)> { |
1828 | let qualname = QualifiedTypeName::from_node(node.clone()); |
1829 | match qualname.members.as_slice() { |
1830 | [unresolved_prop_name] => { |
1831 | let lookup_result = r.borrow().lookup_property(unresolved_prop_name.as_ref()); |
1832 | if !lookup_result.property_type.is_property_type() { |
1833 | diag.push_error(format!("' {}' is not a valid property" , qualname), &node); |
1834 | } else if !lookup_result.is_valid_for_assignment() { |
1835 | diag.push_error( |
1836 | format!( |
1837 | "' {}' cannot be set in a state because it is {}" , |
1838 | qualname, lookup_result.property_visibility |
1839 | ), |
1840 | &node, |
1841 | ); |
1842 | } |
1843 | Some(( |
1844 | NamedReference::new(r, &lookup_result.resolved_name), |
1845 | lookup_result.property_type, |
1846 | )) |
1847 | } |
1848 | [elem_id, unresolved_prop_name] => { |
1849 | if let Some(element) = find_element_by_id(r, elem_id.as_ref()) { |
1850 | let lookup_result = element.borrow().lookup_property(unresolved_prop_name.as_ref()); |
1851 | if !lookup_result.is_valid() { |
1852 | diag.push_error( |
1853 | format!("' {}' not found in ' {}'" , unresolved_prop_name, elem_id), |
1854 | &node, |
1855 | ); |
1856 | } else if !lookup_result.is_valid_for_assignment() { |
1857 | diag.push_error( |
1858 | format!( |
1859 | "' {}' cannot be set in a state because it is {}" , |
1860 | qualname, lookup_result.property_visibility |
1861 | ), |
1862 | &node, |
1863 | ); |
1864 | } |
1865 | Some(( |
1866 | NamedReference::new(&element, &lookup_result.resolved_name), |
1867 | lookup_result.property_type, |
1868 | )) |
1869 | } else { |
1870 | diag.push_error(format!("' {}' is not a valid element id" , elem_id), &node); |
1871 | None |
1872 | } |
1873 | } |
1874 | _ => { |
1875 | diag.push_error(format!("' {}' is not a valid property" , qualname), &node); |
1876 | None |
1877 | } |
1878 | } |
1879 | } |
1880 | |
1881 | /// FIXME: this is duplicated the resolving pass. Also, we should use a hash table |
1882 | fn find_element_by_id(e: &ElementRc, name: &str) -> Option<ElementRc> { |
1883 | if e.borrow().id == name { |
1884 | return Some(e.clone()); |
1885 | } |
1886 | for x: &Rc> in &e.borrow().children { |
1887 | if x.borrow().repeated.is_some() { |
1888 | continue; |
1889 | } |
1890 | if let Some(x: Rc>) = find_element_by_id(e:x, name) { |
1891 | return Some(x); |
1892 | } |
1893 | } |
1894 | |
1895 | None |
1896 | } |
1897 | |
1898 | /// Find the parent element to a given element. |
1899 | /// (since there is no parent mapping we need to fo an exhaustive search) |
1900 | pub fn find_parent_element(e: &ElementRc) -> Option<ElementRc> { |
1901 | fn recurse(base: &ElementRc, e: &ElementRc) -> Option<ElementRc> { |
1902 | for child: &Rc> in &base.borrow().children { |
1903 | if Rc::ptr_eq(this:child, other:e) { |
1904 | return Some(base.clone()); |
1905 | } |
1906 | if let Some(x: Rc>) = recurse(base:child, e) { |
1907 | return Some(x); |
1908 | } |
1909 | } |
1910 | None |
1911 | } |
1912 | |
1913 | let root: Rc> = e.borrow().enclosing_component.upgrade().unwrap().root_element.clone(); |
1914 | if Rc::ptr_eq(&root, other:e) { |
1915 | return None; |
1916 | } |
1917 | recurse(&root, e) |
1918 | } |
1919 | |
1920 | /// Call the visitor for each children of the element recursively, starting with the element itself |
1921 | /// |
1922 | /// The state returned by the visitor is passed to the children |
1923 | pub fn recurse_elem<State>( |
1924 | elem: &ElementRc, |
1925 | state: &State, |
1926 | vis: &mut impl FnMut(&ElementRc, &State) -> State, |
1927 | ) { |
1928 | let state: State = vis(elem, state); |
1929 | for sub: &Rc> in &elem.borrow().children { |
1930 | recurse_elem(elem:sub, &state, vis); |
1931 | } |
1932 | } |
1933 | |
1934 | /// Same as [`recurse_elem`] but include the elements from sub_components |
1935 | pub fn recurse_elem_including_sub_components<State>( |
1936 | component: &Component, |
1937 | state: &State, |
1938 | vis: &mut impl FnMut(&ElementRc, &State) -> State, |
1939 | ) { |
1940 | recurse_elem(&component.root_element, state, &mut |elem: &Rc>, state: &State| { |
1941 | debug_assert!(std::ptr::eq( |
1942 | component as *const Component, |
1943 | (&*elem.borrow().enclosing_component.upgrade().unwrap()) as *const Component |
1944 | )); |
1945 | if elem.borrow().repeated.is_some() { |
1946 | if let ElementType::Component(base: &Rc) = &elem.borrow().base_type { |
1947 | if base.parent_element.upgrade().is_some() { |
1948 | recurse_elem_including_sub_components(component:base, state, vis); |
1949 | } |
1950 | } |
1951 | } |
1952 | vis(elem, state) |
1953 | }); |
1954 | componentIter<'_, PopupWindow> |
1955 | .popup_windows |
1956 | .borrow() |
1957 | .iter() |
1958 | .for_each(|p: &PopupWindow| recurse_elem_including_sub_components(&p.component, state, vis)) |
1959 | } |
1960 | |
1961 | /// Same as recurse_elem, but will take the children from the element as to not keep the element borrow |
1962 | pub fn recurse_elem_no_borrow<State>( |
1963 | elem: &ElementRc, |
1964 | state: &State, |
1965 | vis: &mut impl FnMut(&ElementRc, &State) -> State, |
1966 | ) { |
1967 | let state: State = vis(elem, state); |
1968 | let children: Vec>> = elem.borrow().children.clone(); |
1969 | for sub: &Rc> in &children { |
1970 | recurse_elem_no_borrow(elem:sub, &state, vis); |
1971 | } |
1972 | } |
1973 | |
1974 | /// Same as [`recurse_elem`] but include the elements form sub_components |
1975 | pub fn recurse_elem_including_sub_components_no_borrow<State>( |
1976 | component: &Component, |
1977 | state: &State, |
1978 | vis: &mut impl FnMut(&ElementRc, &State) -> State, |
1979 | ) { |
1980 | recurse_elem_no_borrow(&component.root_element, state, &mut |elem, state| { |
1981 | let base = if elem.borrow().repeated.is_some() { |
1982 | if let ElementType::Component(base) = &elem.borrow().base_type { |
1983 | Some(base.clone()) |
1984 | } else { |
1985 | None |
1986 | } |
1987 | } else { |
1988 | None |
1989 | }; |
1990 | if let Some(base) = base { |
1991 | recurse_elem_including_sub_components_no_borrow(&base, state, vis); |
1992 | } |
1993 | vis(elem, state) |
1994 | }); |
1995 | component |
1996 | .popup_windows |
1997 | .borrow() |
1998 | .iter() |
1999 | .for_each(|p| recurse_elem_including_sub_components_no_borrow(&p.component, state, vis)); |
2000 | component |
2001 | .used_types |
2002 | .borrow() |
2003 | .globals |
2004 | .iter() |
2005 | .for_each(|p| recurse_elem_including_sub_components_no_borrow(p, state, vis)); |
2006 | } |
2007 | |
2008 | /// This visit the binding attached to this element, but does not recurse in children elements |
2009 | /// Also does not recurse within the expressions. |
2010 | /// |
2011 | /// This code will temporarily move the bindings or states member so it can call the visitor without |
2012 | /// maintaining a borrow on the RefCell. |
2013 | pub fn visit_element_expressions( |
2014 | elem: &ElementRc, |
2015 | mut vis: impl FnMut(&mut Expression, Option<&str>, &dyn Fn() -> Type), |
2016 | ) { |
2017 | fn visit_element_expressions_simple( |
2018 | elem: &ElementRc, |
2019 | vis: &mut impl FnMut(&mut Expression, Option<&str>, &dyn Fn() -> Type), |
2020 | ) { |
2021 | for (name, expr) in &elem.borrow().bindings { |
2022 | vis(&mut expr.borrow_mut(), Some(name.as_str()), &|| { |
2023 | elem.borrow().lookup_property(name).property_type |
2024 | }); |
2025 | |
2026 | match &mut expr.borrow_mut().animation { |
2027 | Some(PropertyAnimation::Static(e)) => visit_element_expressions_simple(e, vis), |
2028 | Some(PropertyAnimation::Transition { animations, state_ref }) => { |
2029 | vis(state_ref, None, &|| Type::Int32); |
2030 | for a in animations { |
2031 | visit_element_expressions_simple(&a.animation, vis) |
2032 | } |
2033 | } |
2034 | None => (), |
2035 | } |
2036 | } |
2037 | } |
2038 | |
2039 | let repeated = elem |
2040 | .borrow_mut() |
2041 | .repeated |
2042 | .as_mut() |
2043 | .map(|r| (std::mem::take(&mut r.model), r.is_conditional_element)); |
2044 | if let Some((mut model, is_cond)) = repeated { |
2045 | vis(&mut model, None, &|| if is_cond { Type::Bool } else { Type::Model }); |
2046 | elem.borrow_mut().repeated.as_mut().unwrap().model = model; |
2047 | } |
2048 | visit_element_expressions_simple(elem, &mut vis); |
2049 | let mut states = std::mem::take(&mut elem.borrow_mut().states); |
2050 | for s in &mut states { |
2051 | if let Some(cond) = s.condition.as_mut() { |
2052 | vis(cond, None, &|| Type::Bool) |
2053 | } |
2054 | for (ne, e, _) in &mut s.property_changes { |
2055 | vis(e, Some(ne.name()), &|| { |
2056 | ne.element().borrow().lookup_property(ne.name()).property_type |
2057 | }); |
2058 | } |
2059 | } |
2060 | elem.borrow_mut().states = states; |
2061 | |
2062 | let mut transitions = std::mem::take(&mut elem.borrow_mut().transitions); |
2063 | for t in &mut transitions { |
2064 | for (_, _, a) in &mut t.property_animations { |
2065 | visit_element_expressions_simple(a, &mut vis); |
2066 | } |
2067 | } |
2068 | elem.borrow_mut().transitions = transitions; |
2069 | |
2070 | let component = elem.borrow().enclosing_component.upgrade().unwrap(); |
2071 | if Rc::ptr_eq(&component.root_element, elem) { |
2072 | for e in component.init_code.borrow_mut().iter_mut() { |
2073 | vis(e, None, &|| Type::Void); |
2074 | } |
2075 | } |
2076 | } |
2077 | |
2078 | pub fn visit_named_references_in_expression( |
2079 | expr: &mut Expression, |
2080 | vis: &mut impl FnMut(&mut NamedReference), |
2081 | ) { |
2082 | expr.visit_mut(|sub: &mut Expression| visit_named_references_in_expression(expr:sub, vis)); |
2083 | match expr { |
2084 | Expression::PropertyReference(r: &mut NamedReference) |
2085 | | Expression::CallbackReference(r: &mut NamedReference, _) |
2086 | | Expression::FunctionReference(r: &mut NamedReference, _) => vis(r), |
2087 | Expression::LayoutCacheAccess { layout_cache_prop: &mut NamedReference, .. } => vis(layout_cache_prop), |
2088 | Expression::SolveLayout(l: &mut Layout, _) => l.visit_named_references(visitor:vis), |
2089 | Expression::ComputeLayoutInfo(l: &mut Layout, _) => l.visit_named_references(visitor:vis), |
2090 | // This is not really a named reference, but the result is the same, it need to be updated |
2091 | // FIXME: this should probably be lowered into a PropertyReference |
2092 | Expression::RepeaterModelReference { element: &mut Weak> } |
2093 | | Expression::RepeaterIndexReference { element: &mut Weak> } => { |
2094 | // FIXME: this is questionable |
2095 | let mut nc: NamedReference = NamedReference::new(&element.upgrade().unwrap(), name:"$model" ); |
2096 | vis(&mut nc); |
2097 | debug_assert!(nc.element().borrow().repeated.is_some()); |
2098 | *element = Rc::downgrade(&nc.element()); |
2099 | } |
2100 | _ => {} |
2101 | } |
2102 | } |
2103 | |
2104 | /// Visit all the named reference in an element |
2105 | /// But does not recurse in sub-elements. (unlike [`visit_all_named_references`] which recurse) |
2106 | pub fn visit_all_named_references_in_element( |
2107 | elem: &ElementRc, |
2108 | mut vis: impl FnMut(&mut NamedReference), |
2109 | ) { |
2110 | visit_element_expressions(elem, |expr, _, _| { |
2111 | visit_named_references_in_expression(expr, &mut vis) |
2112 | }); |
2113 | let mut states = std::mem::take(&mut elem.borrow_mut().states); |
2114 | for s in &mut states { |
2115 | for (r, _, _) in &mut s.property_changes { |
2116 | vis(r); |
2117 | } |
2118 | } |
2119 | elem.borrow_mut().states = states; |
2120 | let mut transitions = std::mem::take(&mut elem.borrow_mut().transitions); |
2121 | for t in &mut transitions { |
2122 | for (r, _, _) in &mut t.property_animations { |
2123 | vis(r) |
2124 | } |
2125 | } |
2126 | elem.borrow_mut().transitions = transitions; |
2127 | let mut repeated = std::mem::take(&mut elem.borrow_mut().repeated); |
2128 | if let Some(r) = &mut repeated { |
2129 | if let Some(lv) = &mut r.is_listview { |
2130 | vis(&mut lv.viewport_y); |
2131 | vis(&mut lv.viewport_height); |
2132 | vis(&mut lv.viewport_width); |
2133 | vis(&mut lv.listview_height); |
2134 | vis(&mut lv.listview_width); |
2135 | } |
2136 | } |
2137 | elem.borrow_mut().repeated = repeated; |
2138 | let mut layout_info_prop = std::mem::take(&mut elem.borrow_mut().layout_info_prop); |
2139 | layout_info_prop.as_mut().map(|(h, b)| (vis(h), vis(b))); |
2140 | elem.borrow_mut().layout_info_prop = layout_info_prop; |
2141 | let mut debug = std::mem::take(&mut elem.borrow_mut().debug); |
2142 | for d in debug.iter_mut() { |
2143 | d.1.as_mut().map(|l| l.visit_named_references(&mut vis)); |
2144 | } |
2145 | elem.borrow_mut().debug = debug; |
2146 | |
2147 | let mut accessibility_props = std::mem::take(&mut elem.borrow_mut().accessibility_props); |
2148 | accessibility_props.0.iter_mut().for_each(|(_, x)| vis(x)); |
2149 | elem.borrow_mut().accessibility_props = accessibility_props; |
2150 | |
2151 | let geometry_props = elem.borrow_mut().geometry_props.take(); |
2152 | if let Some(mut geometry_props) = geometry_props { |
2153 | vis(&mut geometry_props.x); |
2154 | vis(&mut geometry_props.y); |
2155 | vis(&mut geometry_props.width); |
2156 | vis(&mut geometry_props.height); |
2157 | elem.borrow_mut().geometry_props = Some(geometry_props); |
2158 | } |
2159 | |
2160 | // visit two way bindings |
2161 | for expr in elem.borrow().bindings.values() { |
2162 | for nr in &mut expr.borrow_mut().two_way_bindings { |
2163 | vis(nr); |
2164 | } |
2165 | } |
2166 | |
2167 | let mut property_declarations = std::mem::take(&mut elem.borrow_mut().property_declarations); |
2168 | for pd in property_declarations.values_mut() { |
2169 | pd.is_alias.as_mut().map(&mut vis); |
2170 | } |
2171 | elem.borrow_mut().property_declarations = property_declarations; |
2172 | } |
2173 | |
2174 | /// Visit all named reference in this component and sub component |
2175 | pub fn visit_all_named_references( |
2176 | component: &Component, |
2177 | vis: &mut impl FnMut(&mut NamedReference), |
2178 | ) { |
2179 | recurse_elem_including_sub_components_no_borrow( |
2180 | component, |
2181 | &Weak::new(), |
2182 | &mut |elem: &Rc>, parent_compo: &Weak| { |
2183 | visit_all_named_references_in_element(elem, |nr: &mut NamedReference| vis(nr)); |
2184 | let compo: Weak = elem.borrow().enclosing_component.clone(); |
2185 | if !Weak::ptr_eq(self:parent_compo, &compo) { |
2186 | let compo: Rc = compo.upgrade().unwrap(); |
2187 | compo.root_constraints.borrow_mut().visit_named_references(visitor:vis); |
2188 | compo.popup_windows.borrow_mut().iter_mut().for_each(|p: &mut PopupWindow| { |
2189 | vis(&mut p.x); |
2190 | vis(&mut p.y); |
2191 | }); |
2192 | } |
2193 | compo |
2194 | }, |
2195 | ); |
2196 | } |
2197 | |
2198 | /// Visit all expression in this component and sub components |
2199 | /// |
2200 | /// Does not recurse in the expression itself |
2201 | pub fn visit_all_expressions( |
2202 | component: &Component, |
2203 | mut vis: impl FnMut(&mut Expression, &dyn Fn() -> Type), |
2204 | ) { |
2205 | recurse_elem_including_sub_components(component, &(), &mut |elem: &Rc>, _| { |
2206 | visit_element_expressions(elem, |expr: &mut Expression, _, ty: &dyn Fn() -> Type| vis(expr, ty)); |
2207 | }) |
2208 | } |
2209 | |
2210 | #[derive (Debug, Clone)] |
2211 | pub struct State { |
2212 | pub id: String, |
2213 | pub condition: Option<Expression>, |
2214 | pub property_changes: Vec<(NamedReference, Expression, syntax_nodes::StatePropertyChange)>, |
2215 | } |
2216 | |
2217 | #[derive (Debug, Clone)] |
2218 | pub struct Transition { |
2219 | /// false for 'to', true for 'out' |
2220 | pub is_out: bool, |
2221 | pub state_id: String, |
2222 | pub property_animations: Vec<(NamedReference, SourceLocation, ElementRc)>, |
2223 | pub node: syntax_nodes::Transition, |
2224 | } |
2225 | |
2226 | impl Transition { |
2227 | fn from_node( |
2228 | trs: syntax_nodes::Transition, |
2229 | r: &ElementRc, |
2230 | tr: &TypeRegister, |
2231 | diag: &mut BuildDiagnostics, |
2232 | ) -> Transition { |
2233 | if let Some(star) = trs.child_token(SyntaxKind::Star) { |
2234 | diag.push_error("catch-all not yet implemented" .into(), &star); |
2235 | }; |
2236 | Transition { |
2237 | is_out: parser::identifier_text(&trs).unwrap_or_default() == "out" , |
2238 | state_id: trs |
2239 | .DeclaredIdentifier() |
2240 | .and_then(|x| parser::identifier_text(&x)) |
2241 | .unwrap_or_default(), |
2242 | property_animations: trs |
2243 | .PropertyAnimation() |
2244 | .flat_map(|pa| pa.QualifiedName().map(move |qn| (pa.clone(), qn))) |
2245 | .filter_map(|(pa, qn)| { |
2246 | lookup_property_from_qualified_name_for_state(qn.clone(), r, diag).and_then( |
2247 | |(ne, prop_type)| { |
2248 | animation_element_from_node(&pa, &qn, prop_type, diag, tr) |
2249 | .map(|anim_element| (ne, qn.to_source_location(), anim_element)) |
2250 | }, |
2251 | ) |
2252 | }) |
2253 | .collect(), |
2254 | node: trs.clone(), |
2255 | } |
2256 | } |
2257 | } |
2258 | |
2259 | #[derive (Clone, Debug, derive_more::Deref)] |
2260 | pub struct ExportedName { |
2261 | #[deref] |
2262 | pub name: String, // normalized |
2263 | pub name_ident: SyntaxNode, |
2264 | } |
2265 | |
2266 | impl ExportedName { |
2267 | pub fn original_name(&self) -> String { |
2268 | self.name_ident |
2269 | .child_token(kind:parser::SyntaxKind::Identifier) |
2270 | .map(|n: SyntaxToken| n.to_string()) |
2271 | .unwrap_or_else(|| self.name.clone()) |
2272 | } |
2273 | } |
2274 | |
2275 | #[derive (Default, Debug, derive_more::Deref)] |
2276 | pub struct Exports { |
2277 | #[deref] |
2278 | components_or_types: Vec<(ExportedName, Either<Rc<Component>, Type>)>, |
2279 | last_exported_component: Option<Rc<Component>>, |
2280 | } |
2281 | |
2282 | impl Exports { |
2283 | pub fn from_node( |
2284 | doc: &syntax_nodes::Document, |
2285 | inner_components: &[Rc<Component>], |
2286 | type_registry: &TypeRegister, |
2287 | diag: &mut BuildDiagnostics, |
2288 | ) -> Self { |
2289 | let resolve_export_to_inner_component_or_import = |
2290 | |internal_name: &str, internal_name_node: &dyn Spanned, diag: &mut BuildDiagnostics| { |
2291 | if let Ok(ElementType::Component(c)) = type_registry.lookup_element(internal_name) { |
2292 | Some(Either::Left(c)) |
2293 | } else if let ty @ Type::Struct { .. } | ty @ Type::Enumeration(_) = |
2294 | type_registry.lookup(internal_name) |
2295 | { |
2296 | Some(Either::Right(ty)) |
2297 | } else if type_registry.lookup_element(internal_name).is_ok() |
2298 | || type_registry.lookup(internal_name) != Type::Invalid |
2299 | { |
2300 | diag.push_error( |
2301 | format!("Cannot export ' {}' because it is not a component" , internal_name,), |
2302 | internal_name_node, |
2303 | ); |
2304 | None |
2305 | } else { |
2306 | diag.push_error(format!("' {}' not found" , internal_name,), internal_name_node); |
2307 | None |
2308 | } |
2309 | }; |
2310 | |
2311 | let mut sorted_exports_with_duplicates: Vec<(ExportedName, _)> = Vec::new(); |
2312 | let mut last_exported_component = None; |
2313 | |
2314 | let mut extend_exports = |
2315 | |it: &mut dyn Iterator<Item = (ExportedName, Either<Rc<Component>, Type>)>| { |
2316 | for (name, compo_or_type) in it { |
2317 | match compo_or_type.as_ref().left() { |
2318 | Some(compo) if !compo.is_global() => { |
2319 | last_exported_component = Some(compo.clone()) |
2320 | } |
2321 | _ => {} |
2322 | } |
2323 | |
2324 | let pos = sorted_exports_with_duplicates |
2325 | .partition_point(|(existing_name, _)| existing_name.name <= name.name); |
2326 | sorted_exports_with_duplicates.insert(pos, (name, compo_or_type)); |
2327 | } |
2328 | }; |
2329 | |
2330 | extend_exports( |
2331 | &mut doc.ExportsList().flat_map(|exports| exports.ExportSpecifier()).filter_map( |
2332 | |export_specifier| { |
2333 | let internal_name = |
2334 | parser::identifier_text(&export_specifier.ExportIdentifier()) |
2335 | .unwrap_or_else(|| { |
2336 | debug_assert!(diag.has_error()); |
2337 | String::new() |
2338 | }); |
2339 | |
2340 | let (name, name_ident): (String, SyntaxNode) = export_specifier |
2341 | .ExportName() |
2342 | .and_then(|ident| { |
2343 | parser::identifier_text(&ident).map(|text| (text, ident.clone().into())) |
2344 | }) |
2345 | .unwrap_or_else(|| { |
2346 | (internal_name.clone(), export_specifier.ExportIdentifier().into()) |
2347 | }); |
2348 | |
2349 | Some(( |
2350 | ExportedName { name, name_ident }, |
2351 | resolve_export_to_inner_component_or_import( |
2352 | &internal_name, |
2353 | &export_specifier.ExportIdentifier(), |
2354 | diag, |
2355 | )?, |
2356 | )) |
2357 | }, |
2358 | ), |
2359 | ); |
2360 | |
2361 | extend_exports(&mut doc.ExportsList().flat_map(|exports| exports.Component()).filter_map( |
2362 | |component| { |
2363 | let name_ident: SyntaxNode = component.DeclaredIdentifier().into(); |
2364 | let name = |
2365 | parser::identifier_text(&component.DeclaredIdentifier()).unwrap_or_else(|| { |
2366 | debug_assert!(diag.has_error()); |
2367 | String::new() |
2368 | }); |
2369 | |
2370 | let compo_or_type = |
2371 | resolve_export_to_inner_component_or_import(&name, &name_ident, diag)?; |
2372 | |
2373 | Some((ExportedName { name, name_ident }, compo_or_type)) |
2374 | }, |
2375 | )); |
2376 | |
2377 | extend_exports( |
2378 | &mut doc |
2379 | .ExportsList() |
2380 | .flat_map(|exports| { |
2381 | exports |
2382 | .StructDeclaration() |
2383 | .map(|st| st.DeclaredIdentifier()) |
2384 | .chain(exports.EnumDeclaration().map(|en| en.DeclaredIdentifier())) |
2385 | }) |
2386 | .filter_map(|name_ident| { |
2387 | let name = parser::identifier_text(&name_ident).unwrap_or_else(|| { |
2388 | debug_assert!(diag.has_error()); |
2389 | String::new() |
2390 | }); |
2391 | |
2392 | let name_ident = name_ident.into(); |
2393 | |
2394 | let compo_or_type = |
2395 | resolve_export_to_inner_component_or_import(&name, &name_ident, diag)?; |
2396 | |
2397 | Some((ExportedName { name, name_ident }, compo_or_type)) |
2398 | }), |
2399 | ); |
2400 | |
2401 | let mut sorted_deduped_exports = Vec::with_capacity(sorted_exports_with_duplicates.len()); |
2402 | let mut it = sorted_exports_with_duplicates.into_iter().peekable(); |
2403 | while let Some((exported_name, compo_or_type)) = it.next() { |
2404 | let mut warning_issued_on_first_occurrence = false; |
2405 | |
2406 | // Skip over duplicates and issue warnings |
2407 | while it.peek().map(|(name, _)| &name.name) == Some(&exported_name.name) { |
2408 | let message = format!("Duplicated export ' {}'" , exported_name.name); |
2409 | |
2410 | if !warning_issued_on_first_occurrence { |
2411 | diag.push_error(message.clone(), &exported_name.name_ident); |
2412 | warning_issued_on_first_occurrence = true; |
2413 | } |
2414 | |
2415 | let duplicate_loc = it.next().unwrap().0.name_ident; |
2416 | diag.push_error(message.clone(), &duplicate_loc); |
2417 | } |
2418 | |
2419 | sorted_deduped_exports.push((exported_name, compo_or_type)); |
2420 | } |
2421 | |
2422 | if sorted_deduped_exports.is_empty() { |
2423 | if let Some(last_compo) = inner_components.last() { |
2424 | if last_compo.is_global() { |
2425 | diag.push_warning( |
2426 | "Global singleton is implicitly marked for export. This is deprecated and it should be explicitly exported" |
2427 | .into(), |
2428 | &last_compo.node, |
2429 | ); |
2430 | } else { |
2431 | diag.push_warning("Component is implicitly marked for export. This is deprecated and it should be explicitly exported" .into(), &last_compo.node) |
2432 | } |
2433 | let name = last_compo.id.clone(); |
2434 | sorted_deduped_exports.push(( |
2435 | ExportedName { name, name_ident: doc.clone().into() }, |
2436 | Either::Left(last_compo.clone()), |
2437 | )) |
2438 | } |
2439 | } |
2440 | |
2441 | if last_exported_component.is_none() { |
2442 | last_exported_component = inner_components.last().cloned(); |
2443 | } |
2444 | |
2445 | Self { components_or_types: sorted_deduped_exports, last_exported_component } |
2446 | } |
2447 | |
2448 | pub fn add_reexports( |
2449 | &mut self, |
2450 | other_exports: impl IntoIterator<Item = (ExportedName, Either<Rc<Component>, Type>)>, |
2451 | diag: &mut BuildDiagnostics, |
2452 | ) { |
2453 | for export in other_exports { |
2454 | match self.components_or_types.binary_search_by(|entry| entry.0.cmp(&export.0)) { |
2455 | Ok(_) => { |
2456 | diag.push_warning( |
2457 | format!( |
2458 | "' {}' is already exported in this file; it will not be re-exported" , |
2459 | &*export.0 |
2460 | ), |
2461 | &export.0.name_ident, |
2462 | ); |
2463 | } |
2464 | Err(insert_pos) => { |
2465 | self.components_or_types.insert(insert_pos, export); |
2466 | } |
2467 | } |
2468 | } |
2469 | } |
2470 | |
2471 | pub fn find(&self, name: &str) -> Option<Either<Rc<Component>, Type>> { |
2472 | self.components_or_types |
2473 | .binary_search_by(|(exported_name, _)| exported_name.as_str().cmp(name)) |
2474 | .ok() |
2475 | .map(|index| self.components_or_types[index].1.clone()) |
2476 | } |
2477 | } |
2478 | |
2479 | impl std::iter::IntoIterator for Exports { |
2480 | type Item = (ExportedName, Either<Rc<Component>, Type>); |
2481 | |
2482 | type IntoIter = std::vec::IntoIter<Self::Item>; |
2483 | |
2484 | fn into_iter(self) -> Self::IntoIter { |
2485 | self.components_or_types.into_iter() |
2486 | } |
2487 | } |
2488 | |
2489 | /// This function replace the root element of a repeated element. the previous root becomes the only |
2490 | /// child of the new root element. |
2491 | /// Note that no reference to the base component must exist outside of repeated_element.base_type |
2492 | pub fn inject_element_as_repeated_element(repeated_element: &ElementRc, new_root: ElementRc) { |
2493 | let component = repeated_element.borrow().base_type.as_component().clone(); |
2494 | // Since we're going to replace the repeated element's component, we need to assert that |
2495 | // outside this function no strong reference exists to it. Then we can unwrap and |
2496 | // replace the root element. |
2497 | debug_assert_eq!(Rc::strong_count(&component), 2); |
2498 | let old_root = &component.root_element; |
2499 | |
2500 | adjust_geometry_for_injected_parent(&new_root, old_root); |
2501 | |
2502 | // Any elements with a weak reference to the repeater's component will need fixing later. |
2503 | let mut elements_with_enclosing_component_reference = Vec::new(); |
2504 | recurse_elem(old_root, &(), &mut |element: &ElementRc, _| { |
2505 | if let Some(enclosing_component) = element.borrow().enclosing_component.upgrade() { |
2506 | if Rc::ptr_eq(&enclosing_component, &component) { |
2507 | elements_with_enclosing_component_reference.push(element.clone()); |
2508 | } |
2509 | } |
2510 | }); |
2511 | elements_with_enclosing_component_reference |
2512 | .extend_from_slice(component.optimized_elements.borrow().as_slice()); |
2513 | elements_with_enclosing_component_reference.push(new_root.clone()); |
2514 | |
2515 | new_root.borrow_mut().child_of_layout = |
2516 | std::mem::replace(&mut old_root.borrow_mut().child_of_layout, false); |
2517 | let layout_info_prop = old_root.borrow().layout_info_prop.clone().or_else(|| { |
2518 | // generate the layout_info_prop that forward to the implicit layout for that item |
2519 | let li_v = crate::layout::create_new_prop( |
2520 | &new_root, |
2521 | "layoutinfo-v" , |
2522 | crate::layout::layout_info_type(), |
2523 | ); |
2524 | let li_h = crate::layout::create_new_prop( |
2525 | &new_root, |
2526 | "layoutinfo-h" , |
2527 | crate::layout::layout_info_type(), |
2528 | ); |
2529 | let expr_h = crate::layout::implicit_layout_info_call(old_root, Orientation::Horizontal); |
2530 | let expr_v = crate::layout::implicit_layout_info_call(old_root, Orientation::Vertical); |
2531 | let expr_v = |
2532 | BindingExpression::new_with_span(expr_v, old_root.borrow().to_source_location()); |
2533 | li_v.element().borrow_mut().bindings.insert(li_v.name().into(), expr_v.into()); |
2534 | let expr_h = |
2535 | BindingExpression::new_with_span(expr_h, old_root.borrow().to_source_location()); |
2536 | li_h.element().borrow_mut().bindings.insert(li_h.name().into(), expr_h.into()); |
2537 | Some((li_h.clone(), li_v.clone())) |
2538 | }); |
2539 | new_root.borrow_mut().layout_info_prop = layout_info_prop; |
2540 | |
2541 | // Replace the repeated component's element with our shadow element. That requires a bit of reference counting |
2542 | // surgery and relies on nobody having a strong reference left to the component, which we take out of the Rc. |
2543 | drop(std::mem::take(&mut repeated_element.borrow_mut().base_type)); |
2544 | |
2545 | debug_assert_eq!(Rc::strong_count(&component), 1); |
2546 | |
2547 | let mut component = Rc::try_unwrap(component).expect("internal compiler error: more than one strong reference left to repeated component when lowering shadow properties" ); |
2548 | |
2549 | let old_root = std::mem::replace(&mut component.root_element, new_root.clone()); |
2550 | new_root.borrow_mut().children.push(old_root); |
2551 | |
2552 | let component = Rc::new(component); |
2553 | repeated_element.borrow_mut().base_type = ElementType::Component(component.clone()); |
2554 | |
2555 | for elem in elements_with_enclosing_component_reference { |
2556 | elem.borrow_mut().enclosing_component = Rc::downgrade(&component); |
2557 | } |
2558 | } |
2559 | |
2560 | /// Make the geometry of the `injected_parent` that of the old_elem. And the old_elem |
2561 | /// will cover the `injected_parent` |
2562 | pub fn adjust_geometry_for_injected_parent(injected_parent: &ElementRc, old_elem: &ElementRc) { |
2563 | let mut injected_parent_mut: RefMut<'_, Element> = injected_parent.borrow_mut(); |
2564 | injected_parent_mut.bindings.insert( |
2565 | key:"z" .into(), |
2566 | value:RefCell::new(BindingExpression::new_two_way(NamedReference::new(element:old_elem, name:"z" ))), |
2567 | ); |
2568 | // (should be removed by const propagation in the llr) |
2569 | injected_parent_mut.property_declarations.insert( |
2570 | key:"dummy" .into(), |
2571 | value:PropertyDeclaration { property_type: Type::LogicalLength, ..Default::default() }, |
2572 | ); |
2573 | let mut old_elem_mut: RefMut<'_, Element> = old_elem.borrow_mut(); |
2574 | injected_parent_mut.default_fill_parent = std::mem::take(&mut old_elem_mut.default_fill_parent); |
2575 | injected_parent_mut.geometry_props = old_elem_mut.geometry_props.clone(); |
2576 | drop(injected_parent_mut); |
2577 | old_elem_mut.geometry_props.as_mut().unwrap().x = NamedReference::new(element:injected_parent, name:"dummy" ); |
2578 | old_elem_mut.geometry_props.as_mut().unwrap().y = NamedReference::new(element:injected_parent, name:"dummy" ); |
2579 | } |
2580 | |