use crate::diagnostics::{FileDiagnostics, Spanned, SpannedWithSourceFile};
use crate::expression_tree::{Expression, ExpressionSpanned, NamedReference};
use crate::parser::{syntax_nodes, SyntaxKind, SyntaxNodeWithSourceFile};
use crate::typeregister::{NativeClass, Type, TypeRegister};
use std::cell::{Cell, RefCell};
use std::collections::HashMap;
use std::rc::{Rc, Weak};
#[derive(Default, Debug)]
pub struct Document {
pub inner_components: Vec<Rc<Component>>,
pub root_component: Rc<Component>,
pub local_registry: TypeRegister,
exports: Exports,
}
impl Document {
pub fn from_node(
node: syntax_nodes::Document,
diag: &mut FileDiagnostics,
parent_registry: &Rc<RefCell<TypeRegister>>,
) -> Self {
debug_assert_eq!(node.kind(), SyntaxKind::Document);
let mut local_registry = TypeRegister::new(parent_registry);
let mut inner_components = vec![];
let mut process_component = |n: syntax_nodes::Component| {
let compo = Component::from_node(n, diag, &local_registry);
local_registry.add(compo.clone());
inner_components.push(compo);
};
for n in node.children() {
match n.kind() {
SyntaxKind::Component => process_component(n.into()),
SyntaxKind::ExportsList => {
syntax_nodes::ExportsList::from(n).Component().for_each(&mut process_component)
}
_ => {}
};
}
let exports = Exports::from_node(&node, &inner_components, &parent_registry, diag);
Document {
root_component: inner_components.last().cloned().unwrap_or_default(),
inner_components,
local_registry,
exports,
}
}
pub fn exports(&self) -> &Vec<(String, Rc<Component>)> {
&self.exports.0
}
}
#[derive(Default, Debug)]
pub struct Component {
pub id: String,
pub root_element: ElementRc,
pub parent_element: Weak<RefCell<Element>>,
pub optimized_elements: RefCell<Vec<ElementRc>>,
pub referenced_file_resources: RefCell<HashMap<String, usize>>,
pub embed_file_resources: Cell<bool>,
pub layouts: RefCell<crate::layout::LayoutVec>,
pub child_insertion_point: RefCell<Option<ElementRc>>,
pub setup_code: RefCell<Vec<Expression>>,
}
impl Component {
pub fn from_node(
node: syntax_nodes::Component,
diag: &mut FileDiagnostics,
tr: &TypeRegister,
) -> Rc<Self> {
let mut child_insertion_point = None;
let c = Component {
id: node.child_text(SyntaxKind::Identifier).unwrap_or_default(),
root_element: Element::from_node(
node.Element(),
"root".into(),
Type::Invalid,
&mut child_insertion_point,
diag,
tr,
),
child_insertion_point: RefCell::new(child_insertion_point),
..Default::default()
};
let c = Rc::new(c);
let weak = Rc::downgrade(&c);
recurse_elem(&c.root_element, &(), &mut |e, _| {
e.borrow_mut().enclosing_component = weak.clone()
});
c
}
}
#[derive(Clone, Debug, Default)]
pub struct PropertyDeclaration {
pub property_type: Type,
pub type_node: Option<SyntaxNodeWithSourceFile>,
pub expose_in_public_api: bool,
pub is_alias: Option<NamedReference>,
}
impl From<Type> for PropertyDeclaration {
fn from(ty: Type) -> Self {
PropertyDeclaration { property_type: ty, ..Self::default() }
}
}
#[derive(Default, Debug)]
pub struct Element {
pub id: String,
pub base_type: crate::typeregister::Type,
pub bindings: HashMap<String, ExpressionSpanned>,
pub children: Vec<ElementRc>,
pub enclosing_component: Weak<Component>,
pub property_declarations: HashMap<String, PropertyDeclaration>,
pub property_animations: HashMap<String, ElementRc>,
pub repeated: Option<RepeatedElementInfo>,
pub states: Vec<State>,
pub transitions: Vec<Transition>,
pub child_of_layout: bool,
pub node: Option<syntax_nodes::Element>,
}
impl Spanned for Element {
fn span(&self) -> crate::diagnostics::Span {
self.node.as_ref().map(|n| n.span()).unwrap_or_default()
}
}
impl SpannedWithSourceFile for Element {
fn source_file(&self) -> Option<&Rc<std::path::PathBuf>> {
self.node.as_ref().map(|n| n.0.source_file.as_ref()).flatten()
}
}
#[derive(Debug, Clone)]
pub struct ListViewInfo {
pub viewport_y: NamedReference,
pub viewport_height: NamedReference,
pub viewport_width: NamedReference,
pub listview_height: NamedReference,
pub listview_width: NamedReference,
}
#[derive(Debug, Clone)]
pub struct RepeatedElementInfo {
pub model: Expression,
pub model_data_id: String,
pub index_id: String,
pub is_conditional_element: bool,
pub is_listview: Option<ListViewInfo>,
}
pub type ElementRc = Rc<RefCell<Element>>;
impl Element {
pub fn from_node(
node: syntax_nodes::Element,
id: String,
parent_type: Type,
component_child_insertion_point: &mut Option<ElementRc>,
diag: &mut FileDiagnostics,
tr: &TypeRegister,
) -> ElementRc {
let base_node = if let Some(base_node) = node.QualifiedName() {
base_node
} else {
if parent_type != Type::Invalid {
assert!(diag.has_error());
return ElementRc::default();
}
let mut error_on = |node: &dyn Spanned, what: &str| {
diag.push_error(
format!(
"A component without base type is a struct declaration and cannot have {}",
what
),
node,
);
};
node.SubElement().for_each(|n| error_on(&n, "sub elements"));
node.RepeatedElement().for_each(|n| error_on(&n, "sub elements"));
node.ChildrenPlaceholder().map(|n| error_on(&n, "sub elements"));
node.SignalConnection().for_each(|n| error_on(&n, "signal connections"));
node.SignalDeclaration().for_each(|n| error_on(&n, "signals"));
node.Binding().for_each(|n| error_on(&n, "bindings"));
node.TwoWayBinding().for_each(|n| error_on(&n, "bindings"));
node.PropertyAnimation().for_each(|n| error_on(&n, "animations"));
node.States().for_each(|n| error_on(&n, "states"));
node.Transitions().for_each(|n| error_on(&n, "transitions"));
let mut r = Element {
id,
base_type: Type::Void,
node: Some(node.clone()),
..Default::default()
};
for prop_decl in node.PropertyDeclaration() {
let type_node = prop_decl.Type();
let prop_name_token =
prop_decl.DeclaredIdentifier().child_token(SyntaxKind::Identifier).unwrap();
let prop_name = prop_name_token.text().to_string();
if !matches!(r.lookup_property(&prop_name), Type::Invalid) {
diag.push_error(
format!("Cannot override property '{}'", prop_name),
&prop_name_token,
)
}
r.property_declarations.insert(
prop_name.clone(),
PropertyDeclaration {
property_type: type_from_node(type_node.clone(), diag, tr),
type_node: Some(type_node.into()),
..Default::default()
},
);
if let Some(csn) = prop_decl.BindingExpression() {
diag.push_error(format!("A component without base type is a struct declaration and cannot have bindings.").into(), &csn);
}
if let Some(csn) = prop_decl.TwoWayBinding() {
diag.push_error(format!("A component without base type is a struct declaration and cannot have bindings.").into(), &csn);
}
}
return Rc::new(RefCell::new(r));
};
let base = QualifiedTypeName::from_node(base_node.clone());
let base_string = base.to_string();
let base_type = match parent_type.lookup_type_for_child_element(&base_string, tr) {
Ok(ty) => ty,
Err(err) => {
diag.push_error(err, &base_node);
return ElementRc::default();
}
};
if let Type::Component(c) = &base_type {
if c.root_element.borrow().base_type == Type::Void {
diag.push_error(
"Cannot create an instance of a struct that does not have a base type".into(),
&base_node,
)
}
}
let mut r = Element { id, base_type, node: Some(node.clone()), ..Default::default() };
assert!(r.base_type.is_object_type());
for prop_decl in node.PropertyDeclaration() {
let type_node = prop_decl.Type();
let prop_type = type_from_node(type_node.clone(), diag, tr);
let prop_name_token =
prop_decl.DeclaredIdentifier().child_token(SyntaxKind::Identifier).unwrap();
let prop_name = prop_name_token.text().to_string();
if !matches!(r.lookup_property(&prop_name), Type::Invalid) {
diag.push_error(
format!("Cannot override property '{}'", prop_name),
&prop_name_token,
)
}
r.property_declarations.insert(
prop_name.clone(),
PropertyDeclaration {
property_type: prop_type,
type_node: Some(type_node.into()),
..Default::default()
},
);
if let Some(csn) = prop_decl.BindingExpression() {
if r.bindings
.insert(prop_name.clone(), ExpressionSpanned::new_uncompiled(csn.into()))
.is_some()
{
diag.push_error("Duplicated property binding".into(), &prop_name_token);
}
}
if let Some(csn) = prop_decl.TwoWayBinding() {
if r.bindings
.insert(prop_name, ExpressionSpanned::new_uncompiled(csn.into()))
.is_some()
{
diag.push_error("Duplicated property binding".into(), &prop_name_token);
}
}
}
r.parse_bindings(
&base,
node.Binding().filter_map(|b| {
Some((b.child_token(SyntaxKind::Identifier)?, b.BindingExpression().into()))
}),
diag,
);
r.parse_bindings(
&base,
node.TwoWayBinding()
.filter_map(|b| Some((b.child_token(SyntaxKind::Identifier)?, b.into()))),
diag,
);
match &r.base_type {
Type::Builtin(builtin_base) => {
for (prop, expr) in &builtin_base.default_bindings {
r.bindings.entry(prop.clone()).or_insert(expr.clone().into());
}
}
_ => {}
}
for sig_decl in node.SignalDeclaration() {
let name_token =
sig_decl.DeclaredIdentifier().child_token(SyntaxKind::Identifier).unwrap();
let name = name_token.text().to_string();
let args = sig_decl.Type().map(|node_ty| type_from_node(node_ty, diag, tr)).collect();
r.property_declarations.insert(
name,
PropertyDeclaration {
property_type: Type::Signal { args },
type_node: Some(sig_decl.into()),
..Default::default()
},
);
}
for con_node in node.SignalConnection() {
let name_token = match con_node.child_token(SyntaxKind::Identifier) {
Some(x) => x,
None => continue,
};
let name = name_token.text().to_string();
let prop_type = r.lookup_property(&name);
if let Type::Signal { args } = prop_type {
let num_arg = con_node.DeclaredIdentifier().count();
if num_arg > args.len() {
diag.push_error(
format!(
"'{}' only has {} arguments, but {} were provided",
name,
args.len(),
num_arg
),
&name_token,
);
}
if r.bindings
.insert(name, ExpressionSpanned::new_uncompiled(con_node.into()))
.is_some()
{
diag.push_error("Duplicated signal".into(), &name_token);
}
} else {
diag.push_error(format!("'{}' is not a signal in {}", name, base), &name_token);
}
}
for anim in node.PropertyAnimation() {
if let Some(star) = anim.child_token(SyntaxKind::Star) {
diag.push_error(
"catch-all property is only allowed within transitions".into(),
&star,
)
};
for prop_name_token in anim.QualifiedName() {
match QualifiedTypeName::from_node(prop_name_token.clone()).members.as_slice() {
[prop_name] => {
let prop_type = r.lookup_property(&prop_name);
if let Some(anim_element) = animation_element_from_node(
&anim,
&prop_name_token,
prop_type,
diag,
tr,
) {
if r.property_animations
.insert(prop_name.clone(), anim_element)
.is_some()
{
diag.push_error("Duplicated animation".into(), &prop_name_token)
}
}
}
_ => diag.push_error(
"Can only refer to property in the current element".into(),
&prop_name_token,
),
}
}
}
let mut children_placeholder = None;
let r = ElementRc::new(RefCell::new(r));
for se in node.children() {
if se.kind() == SyntaxKind::SubElement {
let id = se.child_text(SyntaxKind::Identifier).unwrap_or_default();
if matches!(id.as_ref(), "parent" | "self" | "root") {
diag.push_error(
format!("'{}' is a reserved id", id),
&se.child_token(SyntaxKind::Identifier).unwrap(),
)
}
if let Some(element_node) = se.child_node(SyntaxKind::Element) {
let parent_type = r.borrow().base_type.clone();
r.borrow_mut().children.push(Element::from_node(
element_node.into(),
id,
parent_type,
component_child_insertion_point,
diag,
tr,
));
} else {
assert!(diag.has_error());
}
} else if se.kind() == SyntaxKind::RepeatedElement {
let rep = Element::from_repeated_node(
se.into(),
&r,
component_child_insertion_point,
diag,
tr,
);
r.borrow_mut().children.push(rep);
} else if se.kind() == SyntaxKind::ConditionalElement {
let rep = Element::from_conditional_node(
se.into(),
r.borrow().base_type.clone(),
component_child_insertion_point,
diag,
tr,
);
r.borrow_mut().children.push(rep);
} else if se.kind() == SyntaxKind::ChildrenPlaceholder {
if children_placeholder.is_some() {
diag.push_error(
"The $children placeholder can only appear once in an element".into(),
&se,
)
} else {
children_placeholder = Some(se.clone());
}
}
}
if let Some(children_placeholder) = children_placeholder {
if component_child_insertion_point.is_some() {
diag.push_error(
"The $children placeholder can only appear once in an element hierarchy".into(),
&children_placeholder,
)
} else {
*component_child_insertion_point = Some(r.clone());
}
}
for state in node.States().flat_map(|s| s.State()) {
let s = State {
id: state
.DeclaredIdentifier()
.child_text(SyntaxKind::Identifier)
.unwrap_or_default(),
condition: state.Expression().map(|e| Expression::Uncompiled(e.into())),
property_changes: state
.StatePropertyChange()
.map(|s| {
let (ne, _) =
lookup_property_from_qualified_name(s.QualifiedName(), &r, diag);
(ne, Expression::Uncompiled(s.BindingExpression().into()))
})
.collect(),
};
r.borrow_mut().states.push(s);
}
for trs in node.Transitions().flat_map(|s| s.Transition()) {
if let Some(star) = trs.child_token(SyntaxKind::Star) {
diag.push_error("TODO: catch-all not yet implemented".into(), &star);
};
let trans = Transition {
is_out: trs.child_text(SyntaxKind::Identifier).unwrap_or_default() == "out",
state_id: trs
.DeclaredIdentifier()
.child_text(SyntaxKind::Identifier)
.unwrap_or_default(),
property_animations: trs
.PropertyAnimation()
.flat_map(|pa| pa.QualifiedName().map(move |qn| (pa.clone(), qn)))
.filter_map(|(pa, qn)| {
let (ne, prop_type) =
lookup_property_from_qualified_name(qn.clone(), &r, diag);
if prop_type == Type::Invalid {
debug_assert!(diag.has_error()); return None;
}
animation_element_from_node(&pa, &qn, prop_type, diag, tr)
.map(|anim_element| (ne, anim_element))
})
.collect(),
};
r.borrow_mut().transitions.push(trans);
}
r
}
fn from_repeated_node(
node: syntax_nodes::RepeatedElement,
parent: &ElementRc,
component_child_insertion_point: &mut Option<ElementRc>,
diag: &mut FileDiagnostics,
tr: &TypeRegister,
) -> ElementRc {
let is_listview = if parent.borrow().base_type.to_string() == "ListView" {
Some(ListViewInfo {
viewport_y: NamedReference::new(parent, "viewport_y"),
viewport_height: NamedReference::new(parent, "viewport_height"),
viewport_width: NamedReference::new(parent, "viewport_width"),
listview_height: NamedReference::new(parent, "visible_height"),
listview_width: NamedReference::new(parent, "visible_width"),
})
} else {
None
};
let rei = RepeatedElementInfo {
model: Expression::Uncompiled(node.Expression().into()),
model_data_id: node
.DeclaredIdentifier()
.and_then(|n| n.child_text(SyntaxKind::Identifier))
.unwrap_or_default(),
index_id: node
.RepeatedIndex()
.and_then(|r| r.child_text(SyntaxKind::Identifier))
.unwrap_or_default(),
is_conditional_element: false,
is_listview,
};
let e = Element::from_node(
node.Element(),
String::new(),
parent.borrow().base_type.to_owned(),
component_child_insertion_point,
diag,
tr,
);
e.borrow_mut().repeated = Some(rei);
e
}
fn from_conditional_node(
node: syntax_nodes::ConditionalElement,
parent_type: Type,
component_child_insertion_point: &mut Option<ElementRc>,
diag: &mut FileDiagnostics,
tr: &TypeRegister,
) -> ElementRc {
let rei = RepeatedElementInfo {
model: Expression::Uncompiled(node.Expression().into()),
model_data_id: String::new(),
index_id: String::new(),
is_conditional_element: true,
is_listview: None,
};
let e = Element::from_node(
node.Element(),
String::new(),
parent_type,
component_child_insertion_point,
diag,
tr,
);
e.borrow_mut().repeated = Some(rei);
e
}
pub fn lookup_property(&self, name: &str) -> Type {
self.property_declarations
.get(name)
.cloned()
.map(|decl| decl.property_type)
.unwrap_or_else(|| self.base_type.lookup_property(name))
}
pub fn span(&self) -> crate::diagnostics::Span {
self.node.as_ref().map(|n| n.span()).unwrap_or_default()
}
fn parse_bindings(
&mut self,
base: &QualifiedTypeName,
bindings: impl Iterator<
Item = (crate::parser::SyntaxTokenWithSourceFile, SyntaxNodeWithSourceFile),
>,
diag: &mut FileDiagnostics,
) {
for (name_token, b) in bindings {
let name = name_token.text().to_string();
let prop_type = self.lookup_property(&name);
if !prop_type.is_property_type() {
diag.push_error(
match prop_type {
Type::Invalid => format!("Unknown property {} in {}", name, base),
Type::Signal { .. } => {
format!("'{}' is a signal. Use `=>` to connect", name)
}
_ => format!("Cannot assign to {} in {}", name, base),
},
&name_token,
);
}
if self.bindings.insert(name, ExpressionSpanned::new_uncompiled(b)).is_some() {
diag.push_error("Duplicated property binding".into(), &name_token);
}
}
}
pub fn native_class(&self) -> Option<Rc<NativeClass>> {
let mut base_type = self.base_type.clone();
loop {
match &base_type {
Type::Component(component) => {
base_type = component.root_element.clone().borrow().base_type.clone();
}
Type::Builtin(builtin) => break Some(builtin.native_class.clone()),
Type::Native(native) => break Some(native.clone()),
_ => break None,
}
}
}
}
fn type_from_node(node: syntax_nodes::Type, diag: &mut FileDiagnostics, tr: &TypeRegister) -> Type {
if let Some(qualified_type_node) = node.QualifiedName() {
let qualified_type = QualifiedTypeName::from_node(qualified_type_node.clone());
let prop_type = tr.lookup_qualified(&qualified_type.members);
if prop_type == Type::Invalid {
diag.push_error(
format!("Unknown type '{}'", qualified_type.to_string()),
&qualified_type_node,
);
}
prop_type
} else if let Some(object_node) = node.ObjectType() {
let map = object_node
.ObjectTypeMember()
.map(|member| {
(
member.child_text(SyntaxKind::Identifier).unwrap_or_default(),
type_from_node(member.Type(), diag, tr),
)
})
.collect();
Type::Object(map)
} else if let Some(array_node) = node.ArrayType() {
Type::Array(Box::new(type_from_node(array_node.Type(), diag, tr)))
} else {
assert!(diag.has_error());
Type::Invalid
}
}
fn animation_element_from_node(
anim: &syntax_nodes::PropertyAnimation,
prop_name: &syntax_nodes::QualifiedName,
prop_type: Type,
diag: &mut FileDiagnostics,
tr: &TypeRegister,
) -> Option<ElementRc> {
let anim_type = tr.property_animation_type_for_property(prop_type);
if !matches!(anim_type, Type::Builtin(..)) {
diag.push_error(
format!("'{}' is not an animatable property", prop_name.text().to_string().trim()),
prop_name,
);
None
} else {
let base = QualifiedTypeName {
members: vec![anim_type.as_builtin().native_class.class_name.clone()],
};
let mut anim_element =
Element { id: "".into(), base_type: anim_type, node: None, ..Default::default() };
anim_element.parse_bindings(
&base,
anim.Binding().filter_map(|b| {
Some((b.child_token(SyntaxKind::Identifier)?, b.BindingExpression().into()))
}),
diag,
);
Some(Rc::new(RefCell::new(anim_element)))
}
}
#[derive(Default, Debug, Clone)]
pub struct QualifiedTypeName {
members: Vec<String>,
}
impl QualifiedTypeName {
pub fn from_node(node: syntax_nodes::QualifiedName) -> Self {
debug_assert_eq!(node.kind(), SyntaxKind::QualifiedName);
let members = node
.children_with_tokens()
.filter(|n| n.kind() == SyntaxKind::Identifier)
.filter_map(|x| x.as_token().map(|x| x.text().to_string()))
.collect();
Self { members }
}
}
impl std::fmt::Display for QualifiedTypeName {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.members.join("."))
}
}
fn lookup_property_from_qualified_name(
node: syntax_nodes::QualifiedName,
r: &Rc<RefCell<Element>>,
diag: &mut FileDiagnostics,
) -> (NamedReference, Type) {
let qualname = QualifiedTypeName::from_node(node.clone());
match qualname.members.as_slice() {
[prop_name] => {
let ty = r.borrow().lookup_property(prop_name.as_ref());
if !ty.is_property_type() {
diag.push_error(format!("'{}' is not a valid property", qualname), &node);
}
(NamedReference { element: Rc::downgrade(&r), name: prop_name.clone() }, ty)
}
[elem_id, prop_name] => {
let (element, ty) = if let Some(element) = find_element_by_id(&r, elem_id.as_ref()) {
let ty = element.borrow().lookup_property(prop_name.as_ref());
if !ty.is_property_type() {
diag.push_error(format!("'{}' not found in '{}'", prop_name, elem_id), &node);
}
(Rc::downgrade(&element), ty)
} else {
diag.push_error(format!("'{}' is not a valid element id", elem_id), &node);
(Weak::new(), Type::Invalid)
};
(NamedReference { element, name: prop_name.clone() }, ty)
}
_ => {
diag.push_error(format!("'{}' is not a valid property", qualname), &node);
(NamedReference { element: Default::default(), name: String::default() }, Type::Invalid)
}
}
}
fn find_element_by_id(e: &ElementRc, name: &str) -> Option<ElementRc> {
if e.borrow().id == name {
return Some(e.clone());
}
for x in &e.borrow().children {
if x.borrow().repeated.is_some() {
continue;
}
if let Some(x) = find_element_by_id(x, name) {
return Some(x);
}
}
None
}
pub fn recurse_elem<State>(
elem: &ElementRc,
state: &State,
vis: &mut impl FnMut(&ElementRc, &State) -> State,
) {
let state = vis(elem, state);
for sub in &elem.borrow().children {
recurse_elem(sub, &state, vis);
}
}
pub fn recurse_elem_including_sub_components<State>(
elem: &ElementRc,
state: &State,
vis: &mut impl FnMut(&ElementRc, &State) -> State,
) {
let state = vis(elem, state);
for sub in &elem.borrow().children {
recurse_elem(sub, &state, &mut |elem, state| {
if elem.borrow().repeated.is_some() {
if let Type::Component(base) = &elem.borrow().base_type {
recurse_elem_including_sub_components(&base.root_element, state, vis);
}
}
vis(elem, state)
});
}
}
pub fn recurse_elem_no_borrow<State>(
elem: &ElementRc,
state: &State,
vis: &mut impl FnMut(&ElementRc, &State) -> State,
) {
let state = vis(elem, state);
let children = std::mem::take(&mut elem.borrow_mut().children);
for sub in &children {
recurse_elem_no_borrow(sub, &state, vis);
}
elem.borrow_mut().children = children;
}
pub fn recurse_elem_including_sub_components_no_borrow<State>(
elem: &ElementRc,
state: &State,
vis: &mut impl FnMut(&ElementRc, &State) -> State,
) {
let state = vis(elem, state);
for sub in &elem.borrow().children {
recurse_elem_no_borrow(sub, &state, &mut |elem, state| {
if elem.borrow().repeated.is_some() {
if let Type::Component(base) = &elem.borrow().base_type {
recurse_elem_including_sub_components_no_borrow(&base.root_element, state, vis);
}
}
vis(elem, state)
});
}
}
pub fn visit_element_expressions(
elem: &ElementRc,
mut vis: impl FnMut(&mut Expression, Option<&str>, &dyn Fn() -> Type),
) {
let repeated = std::mem::take(&mut elem.borrow_mut().repeated);
if let Some(mut r) = repeated {
let is_conditional_element = r.is_conditional_element;
vis(&mut r.model, None, &|| if is_conditional_element { Type::Bool } else { Type::Model });
elem.borrow_mut().repeated = Some(r)
}
let mut bindings = std::mem::take(&mut elem.borrow_mut().bindings);
for (name, expr) in &mut bindings {
vis(expr, Some(name.as_str()), &|| elem.borrow().lookup_property(name));
}
elem.borrow_mut().bindings = bindings;
let mut states = std::mem::take(&mut elem.borrow_mut().states);
for s in &mut states {
if let Some(cond) = s.condition.as_mut() {
vis(cond, None, &|| Type::Bool)
}
for (ne, e) in &mut s.property_changes {
vis(e, Some(ne.name.as_ref()), &|| {
ne.element.upgrade().unwrap().borrow().lookup_property(ne.name.as_ref())
});
}
}
elem.borrow_mut().states = states;
let property_animations = std::mem::take(&mut elem.borrow_mut().property_animations);
for anim_elem in property_animations.values() {
let mut bindings = std::mem::take(&mut anim_elem.borrow_mut().bindings);
for (name, expr) in &mut bindings {
vis(expr, Some(name.as_str()), &|| anim_elem.borrow().lookup_property(name));
}
anim_elem.borrow_mut().bindings = bindings;
}
elem.borrow_mut().property_animations = property_animations;
}
pub fn visit_all_named_references(elem: &ElementRc, mut vis: impl FnMut(&mut NamedReference)) {
fn recurse_expression(expr: &mut Expression, vis: &mut impl FnMut(&mut NamedReference)) {
expr.visit_mut(|sub| recurse_expression(sub, vis));
match expr {
Expression::PropertyReference(r) | Expression::SignalReference(r) => vis(r),
Expression::TwoWayBinding(r, _) => vis(r),
Expression::RepeaterModelReference { element }
| Expression::RepeaterIndexReference { element } => {
let mut nc = NamedReference { element: element.clone(), name: "$model".into() };
vis(&mut nc);
debug_assert!(nc.element.upgrade().unwrap().borrow().repeated.is_some());
*element = nc.element;
}
_ => {}
}
}
visit_element_expressions(elem, |expr, _, _| recurse_expression(expr, &mut vis));
let mut states = std::mem::take(&mut elem.borrow_mut().states);
for s in &mut states {
for (r, _) in &mut s.property_changes {
vis(r);
}
}
elem.borrow_mut().states = states;
let mut transitions = std::mem::take(&mut elem.borrow_mut().transitions);
for t in &mut transitions {
for (r, _) in &mut t.property_animations {
vis(r)
}
}
elem.borrow_mut().transitions = transitions;
let mut repeated = std::mem::take(&mut elem.borrow_mut().repeated);
if let Some(r) = &mut repeated {
if let Some(lv) = &mut r.is_listview {
vis(&mut lv.viewport_y);
vis(&mut lv.viewport_height);
vis(&mut lv.viewport_width);
vis(&mut lv.listview_height);
vis(&mut lv.listview_width);
}
}
elem.borrow_mut().repeated = repeated;
}
#[derive(Debug, Clone)]
pub struct State {
pub id: String,
pub condition: Option<Expression>,
pub property_changes: Vec<(NamedReference, Expression)>,
}
#[derive(Debug)]
pub struct Transition {
pub is_out: bool,
pub state_id: String,
pub property_animations: Vec<(NamedReference, ElementRc)>,
}
#[derive(Debug, Clone)]
pub struct NamedExport {
pub internal_name: String,
pub exported_name: String,
}
#[derive(Default, Debug, derive_more::Deref)]
pub struct Exports(Vec<(String, Rc<Component>)>);
impl Exports {
pub fn from_node(
doc: &syntax_nodes::Document,
inner_components: &Vec<Rc<Component>>,
type_registry: &Rc<RefCell<TypeRegister>>,
diag: &mut FileDiagnostics,
) -> Self {
let mut exports = doc
.ExportsList()
.flat_map(|exports| exports.ExportSpecifier())
.filter_map(|export_specifier| {
let internal_name =
match export_specifier.ExportIdentifier().child_text(SyntaxKind::Identifier) {
Some(name) => name,
_ => {
diag.push_error(
"Missing internal name for export".to_owned(),
&export_specifier.ExportIdentifier(),
);
return None;
}
};
let exported_name = match export_specifier.ExportName() {
Some(ident) => match ident.child_text(SyntaxKind::Identifier) {
Some(name) => name,
None => {
diag.push_error("Missing external name for export".to_owned(), &ident);
return None;
}
},
None => internal_name.clone(),
};
Some(NamedExport { internal_name, exported_name })
})
.collect::<Vec<_>>();
exports.extend(doc.ExportsList().flat_map(|exports| exports.Component()).filter_map(
|component| {
let name = match component.child_text(SyntaxKind::Identifier) {
Some(name) => name,
None => {
diag.push_error(
"Cannot export component without name".to_owned(),
&component,
);
return None;
}
};
Some(NamedExport { internal_name: name.clone(), exported_name: name })
},
));
if exports.is_empty() {
let internal_name = inner_components.last().cloned().unwrap_or_default().id.clone();
exports.push(NamedExport {
internal_name: internal_name.clone(),
exported_name: internal_name,
})
}
let imported_names = doc
.ImportSpecifier()
.map(|import| crate::typeloader::ImportedName::extract_imported_names(&import))
.flatten()
.collect::<Vec<_>>();
let resolve_export_to_inner_component_or_import = |export: &NamedExport| {
if let Some(local_comp) = inner_components.iter().find(|c| c.id == export.internal_name)
{
local_comp.clone()
} else {
imported_names
.iter()
.find_map(|import| {
if import.internal_name == export.internal_name {
Some(
type_registry
.borrow()
.lookup_element(&import.internal_name)
.unwrap()
.as_component()
.clone(),
)
} else {
None
}
})
.unwrap()
}
};
Self(
exports
.iter()
.map(|export| {
(
export.exported_name.clone(),
resolve_export_to_inner_component_or_import(export),
)
})
.collect(),
)
}
}