mirror of
				https://github.com/slint-ui/slint.git
				synced 2025-11-03 05:12:55 +00:00 
			
		
		
		
	
							parent
							
								
									bef2e3617d
								
							
						
					
					
						commit
						3a4f3c61d5
					
				
					 17 changed files with 196 additions and 16 deletions
				
			
		| 
						 | 
					@ -138,6 +138,8 @@ log = { version = "0.4.17", optional = true }
 | 
				
			||||||
[dev-dependencies]
 | 
					[dev-dependencies]
 | 
				
			||||||
slint-build = { path = "../build" }
 | 
					slint-build = { path = "../build" }
 | 
				
			||||||
i-slint-backend-testing = { version = "=1.0.3", path = "../../../internal/backends/testing" }
 | 
					i-slint-backend-testing = { version = "=1.0.3", path = "../../../internal/backends/testing" }
 | 
				
			||||||
 | 
					serde_json = "1.0.96"
 | 
				
			||||||
 | 
					serde = { version = "1.0.163", features = ["derive"] }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[package.metadata.docs.rs]
 | 
					[package.metadata.docs.rs]
 | 
				
			||||||
rustdoc-args = [
 | 
					rustdoc-args = [
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -208,6 +208,26 @@ struct MyStruct {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The `.slint` file allows you to utilize Rust attributes and features for defining structures using the `@rust-attr()` directive.
 | 
				
			||||||
 | 
					This enables you to customize the generated code by applying additional traits, derivations, or annotations.
 | 
				
			||||||
 | 
					Consider the following structure defined in the `.slint` file with Rust attributes:
 | 
				
			||||||
 | 
					```slint,ignore
 | 
				
			||||||
 | 
					@rust-attr(derive(serde::Serialize, serde::Deserialize))
 | 
				
			||||||
 | 
					struct MyStruct {
 | 
				
			||||||
 | 
					    foo : i32,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Based on this structure, the following Rust code would be generated:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```rust
 | 
				
			||||||
 | 
					#[derive(serde::Serialize, serde::Deserialize)]
 | 
				
			||||||
 | 
					#[derive(Default, Clone, Debug, PartialEq)]
 | 
				
			||||||
 | 
					struct MyStruct {
 | 
				
			||||||
 | 
					    foo : i32,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Exported Global singletons
 | 
					## Exported Global singletons
 | 
				
			||||||
 | 
					
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -15,3 +15,19 @@ fn empty_stuff() {
 | 
				
			||||||
    slint!(export struct Hei { abcd: bool });
 | 
					    slint!(export struct Hei { abcd: bool });
 | 
				
			||||||
    slint!(export global G { });
 | 
					    slint!(export global G { });
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[test]
 | 
				
			||||||
 | 
					fn test_serialize_deserialize_struct() {
 | 
				
			||||||
 | 
					    i_slint_backend_testing::init();
 | 
				
			||||||
 | 
					    slint! {
 | 
				
			||||||
 | 
					        @rust-attr(derive(serde::Serialize, serde::Deserialize))
 | 
				
			||||||
 | 
					        export struct TestStruct {
 | 
				
			||||||
 | 
					            foo: int,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        export component Test { }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    let data = TestStruct { foo: 1 };
 | 
				
			||||||
 | 
					    let serialized = serde_json::to_string(&data).unwrap();
 | 
				
			||||||
 | 
					    let deserialized: TestStruct = serde_json::from_str(&serialized).unwrap();
 | 
				
			||||||
 | 
					    assert_eq!(data, deserialized);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -173,6 +173,7 @@ impl BuiltinFunction {
 | 
				
			||||||
                    .collect(),
 | 
					                    .collect(),
 | 
				
			||||||
                    name: Some("Size".to_string()),
 | 
					                    name: Some("Size".to_string()),
 | 
				
			||||||
                    node: None,
 | 
					                    node: None,
 | 
				
			||||||
 | 
					                    rust_attributes: None,
 | 
				
			||||||
                }),
 | 
					                }),
 | 
				
			||||||
                args: vec![Type::Image],
 | 
					                args: vec![Type::Image],
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
| 
						 | 
					@ -1027,7 +1028,7 @@ impl Expression {
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                (
 | 
					                (
 | 
				
			||||||
                    Type::Struct { fields: ref left, .. },
 | 
					                    Type::Struct { fields: ref left, .. },
 | 
				
			||||||
                    Type::Struct { fields: right, name, node: n },
 | 
					                    Type::Struct { fields: right, name, node: n, rust_attributes },
 | 
				
			||||||
                ) if left != right => {
 | 
					                ) if left != right => {
 | 
				
			||||||
                    if let Expression::Struct { mut values, .. } = self {
 | 
					                    if let Expression::Struct { mut values, .. } = self {
 | 
				
			||||||
                        let mut new_values = HashMap::new();
 | 
					                        let mut new_values = HashMap::new();
 | 
				
			||||||
| 
						 | 
					@ -1051,6 +1052,7 @@ impl Expression {
 | 
				
			||||||
                                        fields: left.clone(),
 | 
					                                        fields: left.clone(),
 | 
				
			||||||
                                        name: name.clone(),
 | 
					                                        name: name.clone(),
 | 
				
			||||||
                                        node: n.clone(),
 | 
					                                        node: n.clone(),
 | 
				
			||||||
 | 
					                                        rust_attributes: rust_attributes.clone(),
 | 
				
			||||||
                                    },
 | 
					                                    },
 | 
				
			||||||
                                }),
 | 
					                                }),
 | 
				
			||||||
                                name: key.clone(),
 | 
					                                name: key.clone(),
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -579,7 +579,7 @@ pub fn generate(doc: &Document) -> impl std::fmt::Display {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for ty in doc.root_component.used_types.borrow().structs.iter() {
 | 
					    for ty in doc.root_component.used_types.borrow().structs.iter() {
 | 
				
			||||||
        if let Type::Struct { fields, name: Some(name), node: Some(node) } = ty {
 | 
					        if let Type::Struct { fields, name: Some(name), node: Some(node), .. } = ty {
 | 
				
			||||||
            generate_struct(&mut file, name, fields, node);
 | 
					            generate_struct(&mut file, name, fields, node);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -25,6 +25,7 @@ use proc_macro2::{Ident, TokenStream};
 | 
				
			||||||
use quote::{format_ident, quote};
 | 
					use quote::{format_ident, quote};
 | 
				
			||||||
use std::collections::BTreeMap;
 | 
					use std::collections::BTreeMap;
 | 
				
			||||||
use std::num::NonZeroUsize;
 | 
					use std::num::NonZeroUsize;
 | 
				
			||||||
 | 
					use std::str::FromStr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type EvaluationContext<'a> = llr_EvaluationContext<'a, TokenStream>;
 | 
					type EvaluationContext<'a> = llr_EvaluationContext<'a, TokenStream>;
 | 
				
			||||||
type ParentCtx<'a> = llr_ParentCtx<'a, TokenStream>;
 | 
					type ParentCtx<'a> = llr_ParentCtx<'a, TokenStream>;
 | 
				
			||||||
| 
						 | 
					@ -139,8 +140,8 @@ pub fn generate(doc: &Document) -> TokenStream {
 | 
				
			||||||
        .structs
 | 
					        .structs
 | 
				
			||||||
        .iter()
 | 
					        .iter()
 | 
				
			||||||
        .filter_map(|ty| {
 | 
					        .filter_map(|ty| {
 | 
				
			||||||
            if let Type::Struct { fields, name: Some(name), node: Some(_) } = ty {
 | 
					            if let Type::Struct { fields, name: Some(name), node: Some(_), rust_attributes } = ty {
 | 
				
			||||||
                Some((ident(name), generate_struct(name, fields)))
 | 
					                Some((ident(name), generate_struct(name, fields, rust_attributes)))
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
                None
 | 
					                None
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
| 
						 | 
					@ -422,12 +423,28 @@ fn generate_public_component(llr: &llr::PublicComponent) -> TokenStream {
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn generate_struct(name: &str, fields: &BTreeMap<String, Type>) -> TokenStream {
 | 
					fn generate_struct(
 | 
				
			||||||
 | 
					    name: &str,
 | 
				
			||||||
 | 
					    fields: &BTreeMap<String, Type>,
 | 
				
			||||||
 | 
					    rust_attributes: &Option<Vec<String>>,
 | 
				
			||||||
 | 
					) -> TokenStream {
 | 
				
			||||||
    let component_id = struct_name_to_tokens(name);
 | 
					    let component_id = struct_name_to_tokens(name);
 | 
				
			||||||
    let (declared_property_vars, declared_property_types): (Vec<_>, Vec<_>) =
 | 
					    let (declared_property_vars, declared_property_types): (Vec<_>, Vec<_>) =
 | 
				
			||||||
        fields.iter().map(|(name, ty)| (ident(name), rust_primitive_type(ty).unwrap())).unzip();
 | 
					        fields.iter().map(|(name, ty)| (ident(name), rust_primitive_type(ty).unwrap())).unzip();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let attributes = if let Some(feature) = rust_attributes {
 | 
				
			||||||
 | 
					        let attr =
 | 
				
			||||||
 | 
					            feature.iter().map(|f| match TokenStream::from_str(format!(r#"#[{}]"#, f).as_str()) {
 | 
				
			||||||
 | 
					                Ok(eval) => eval,
 | 
				
			||||||
 | 
					                Err(_) => quote! {},
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        quote! { #(#attr)* }
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        quote! {}
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    quote! {
 | 
					    quote! {
 | 
				
			||||||
 | 
					        #attributes
 | 
				
			||||||
        #[derive(Default, PartialEq, Debug, Clone)]
 | 
					        #[derive(Default, PartialEq, Debug, Clone)]
 | 
				
			||||||
        pub struct #component_id {
 | 
					        pub struct #component_id {
 | 
				
			||||||
            #(pub #declared_property_vars : #declared_property_types),*
 | 
					            #(pub #declared_property_vars : #declared_property_types),*
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -60,6 +60,8 @@ pub enum Type {
 | 
				
			||||||
        name: Option<String>,
 | 
					        name: Option<String>,
 | 
				
			||||||
        /// When declared in .slint, this is the node of the declaration.
 | 
					        /// When declared in .slint, this is the node of the declaration.
 | 
				
			||||||
        node: Option<syntax_nodes::ObjectType>,
 | 
					        node: Option<syntax_nodes::ObjectType>,
 | 
				
			||||||
 | 
					        /// deriven
 | 
				
			||||||
 | 
					        rust_attributes: Option<Vec<String>>,
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    Enumeration(Rc<Enumeration>),
 | 
					    Enumeration(Rc<Enumeration>),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -104,8 +106,8 @@ impl core::cmp::PartialEq for Type {
 | 
				
			||||||
            Type::Easing => matches!(other, Type::Easing),
 | 
					            Type::Easing => matches!(other, Type::Easing),
 | 
				
			||||||
            Type::Brush => matches!(other, Type::Brush),
 | 
					            Type::Brush => matches!(other, Type::Brush),
 | 
				
			||||||
            Type::Array(a) => matches!(other, Type::Array(b) if a == b),
 | 
					            Type::Array(a) => matches!(other, Type::Array(b) if a == b),
 | 
				
			||||||
            Type::Struct { fields, name, node: _ } => {
 | 
					            Type::Struct { fields, name, node: _, rust_attributes: _ } => {
 | 
				
			||||||
                matches!(other, Type::Struct{fields: f, name: n, node: _} if fields == f && name == n)
 | 
					                matches!(other, Type::Struct{fields:f,name:n,node:_, rust_attributes: _ } if fields == f && name == n)
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            Type::Enumeration(lhs) => matches!(other, Type::Enumeration(rhs) if lhs == rhs),
 | 
					            Type::Enumeration(lhs) => matches!(other, Type::Enumeration(rhs) if lhs == rhs),
 | 
				
			||||||
            Type::UnitProduct(a) => matches!(other, Type::UnitProduct(b) if a == b),
 | 
					            Type::UnitProduct(a) => matches!(other, Type::UnitProduct(b) if a == b),
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -460,6 +460,7 @@ pub fn layout_info_type() -> Type {
 | 
				
			||||||
            .collect(),
 | 
					            .collect(),
 | 
				
			||||||
        name: Some("LayoutInfo".into()),
 | 
					        name: Some("LayoutInfo".into()),
 | 
				
			||||||
        node: None,
 | 
					        node: None,
 | 
				
			||||||
 | 
					        rust_attributes: None,
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -398,6 +398,7 @@ pub fn lower_animation(a: &PropertyAnimation, ctx: &ExpressionContext<'_>) -> An
 | 
				
			||||||
            fields: animation_fields().collect(),
 | 
					            fields: animation_fields().collect(),
 | 
				
			||||||
            name: Some("PropertyAnimation".into()),
 | 
					            name: Some("PropertyAnimation".into()),
 | 
				
			||||||
            node: None,
 | 
					            node: None,
 | 
				
			||||||
 | 
					            rust_attributes: None,
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -435,6 +436,7 @@ pub fn lower_animation(a: &PropertyAnimation, ctx: &ExpressionContext<'_>) -> An
 | 
				
			||||||
                    .collect(),
 | 
					                    .collect(),
 | 
				
			||||||
                    name: None,
 | 
					                    name: None,
 | 
				
			||||||
                    node: None,
 | 
					                    node: None,
 | 
				
			||||||
 | 
					                    rust_attributes: None,
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                values: IntoIterator::into_iter([
 | 
					                values: IntoIterator::into_iter([
 | 
				
			||||||
                    ("0".to_string(), get_anim),
 | 
					                    ("0".to_string(), get_anim),
 | 
				
			||||||
| 
						 | 
					@ -661,6 +663,7 @@ fn box_layout_data(
 | 
				
			||||||
        .collect(),
 | 
					        .collect(),
 | 
				
			||||||
        name: Some("BoxLayoutCellData".into()),
 | 
					        name: Some("BoxLayoutCellData".into()),
 | 
				
			||||||
        node: None,
 | 
					        node: None,
 | 
				
			||||||
 | 
					        rust_attributes: None,
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if repeater_count == 0 {
 | 
					    if repeater_count == 0 {
 | 
				
			||||||
| 
						 | 
					@ -747,6 +750,7 @@ pub(super) fn grid_layout_cell_data_ty() -> Type {
 | 
				
			||||||
        .collect(),
 | 
					        .collect(),
 | 
				
			||||||
        name: Some("GridLayoutCellData".into()),
 | 
					        name: Some("GridLayoutCellData".into()),
 | 
				
			||||||
        node: None,
 | 
					        node: None,
 | 
				
			||||||
 | 
					        rust_attributes: None,
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -843,6 +847,7 @@ fn compile_path(path: &crate::expression_tree::Path, ctx: &ExpressionContext) ->
 | 
				
			||||||
                    fields: Default::default(),
 | 
					                    fields: Default::default(),
 | 
				
			||||||
                    name: Some("PathElement".to_owned()),
 | 
					                    name: Some("PathElement".to_owned()),
 | 
				
			||||||
                    node: None,
 | 
					                    node: None,
 | 
				
			||||||
 | 
					                    rust_attributes: None,
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                values: elements,
 | 
					                values: elements,
 | 
				
			||||||
                as_model: false,
 | 
					                as_model: false,
 | 
				
			||||||
| 
						 | 
					@ -866,6 +871,7 @@ fn compile_path(path: &crate::expression_tree::Path, ctx: &ExpressionContext) ->
 | 
				
			||||||
                            .collect(),
 | 
					                            .collect(),
 | 
				
			||||||
                        name: element.element_type.native_class.cpp_type.clone(),
 | 
					                        name: element.element_type.native_class.cpp_type.clone(),
 | 
				
			||||||
                        node: None,
 | 
					                        node: None,
 | 
				
			||||||
 | 
					                        rust_attributes: None,
 | 
				
			||||||
                    };
 | 
					                    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    llr_Expression::Struct {
 | 
					                    llr_Expression::Struct {
 | 
				
			||||||
| 
						 | 
					@ -917,6 +923,7 @@ fn compile_path(path: &crate::expression_tree::Path, ctx: &ExpressionContext) ->
 | 
				
			||||||
                        .collect(),
 | 
					                        .collect(),
 | 
				
			||||||
                        name: None,
 | 
					                        name: None,
 | 
				
			||||||
                        node: None,
 | 
					                        node: None,
 | 
				
			||||||
 | 
					                        rust_attributes: None,
 | 
				
			||||||
                    },
 | 
					                    },
 | 
				
			||||||
                    values: IntoIterator::into_iter([
 | 
					                    values: IntoIterator::into_iter([
 | 
				
			||||||
                        (
 | 
					                        (
 | 
				
			||||||
| 
						 | 
					@ -960,5 +967,8 @@ fn make_struct(
 | 
				
			||||||
        values.insert(name.to_string(), expr);
 | 
					        values.insert(name.to_string(), expr);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    llr_Expression::Struct { ty: Type::Struct { fields, name: Some(name), node: None }, values }
 | 
					    llr_Expression::Struct {
 | 
				
			||||||
 | 
					        ty: Type::Struct { fields, name: Some(name), node: None, rust_attributes: None },
 | 
				
			||||||
 | 
					        values,
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -36,7 +36,7 @@ pub fn load_builtins(register: &mut TypeRegister) {
 | 
				
			||||||
    // parse structs
 | 
					    // parse structs
 | 
				
			||||||
    for s in doc.StructDeclaration().chain(doc.ExportsList().flat_map(|e| e.StructDeclaration())) {
 | 
					    for s in doc.StructDeclaration().chain(doc.ExportsList().flat_map(|e| e.StructDeclaration())) {
 | 
				
			||||||
        let external_name = identifier_text(&s.DeclaredIdentifier()).unwrap();
 | 
					        let external_name = identifier_text(&s.DeclaredIdentifier()).unwrap();
 | 
				
			||||||
        let mut ty = object_tree::type_struct_from_node(s.ObjectType(), &mut diag, register);
 | 
					        let mut ty = object_tree::type_struct_from_node(s.ObjectType(), &mut diag, register, None);
 | 
				
			||||||
        if let Type::Struct { name, .. } = &mut ty {
 | 
					        if let Type::Struct { name, .. } = &mut ty {
 | 
				
			||||||
            *name = Some(
 | 
					            *name = Some(
 | 
				
			||||||
                parse_annotation("name", &s.ObjectType())
 | 
					                parse_annotation("name", &s.ObjectType())
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -7,7 +7,7 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// cSpell: ignore qualname
 | 
					// cSpell: ignore qualname
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use itertools::Either;
 | 
					use itertools::{Either, Itertools};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::diagnostics::{BuildDiagnostics, SourceLocation, Spanned};
 | 
					use crate::diagnostics::{BuildDiagnostics, SourceLocation, Spanned};
 | 
				
			||||||
use crate::expression_tree::{self, BindingExpression, Expression, Unit};
 | 
					use crate::expression_tree::{self, BindingExpression, Expression, Unit};
 | 
				
			||||||
| 
						 | 
					@ -77,7 +77,21 @@ impl Document {
 | 
				
			||||||
            |n: syntax_nodes::StructDeclaration,
 | 
					            |n: syntax_nodes::StructDeclaration,
 | 
				
			||||||
             diag: &mut BuildDiagnostics,
 | 
					             diag: &mut BuildDiagnostics,
 | 
				
			||||||
             local_registry: &mut TypeRegister| {
 | 
					             local_registry: &mut TypeRegister| {
 | 
				
			||||||
                let mut ty = type_struct_from_node(n.ObjectType(), diag, local_registry);
 | 
					                let rust_attributes: Vec<String> = n
 | 
				
			||||||
 | 
					                    .children()
 | 
				
			||||||
 | 
					                    .filter(|child| child.kind() == SyntaxKind::AtRustAttr)
 | 
				
			||||||
 | 
					                    .map(|child| {
 | 
				
			||||||
 | 
					                        let mut text = child.text().to_string();
 | 
				
			||||||
 | 
					                        text.pop();
 | 
				
			||||||
 | 
					                        text
 | 
				
			||||||
 | 
					                    })
 | 
				
			||||||
 | 
					                    .collect_vec();
 | 
				
			||||||
 | 
					                let mut ty = type_struct_from_node(
 | 
				
			||||||
 | 
					                    n.ObjectType(),
 | 
				
			||||||
 | 
					                    diag,
 | 
				
			||||||
 | 
					                    local_registry,
 | 
				
			||||||
 | 
					                    Some(rust_attributes),
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
                if let Type::Struct { name, .. } = &mut ty {
 | 
					                if let Type::Struct { name, .. } = &mut ty {
 | 
				
			||||||
                    *name = parser::identifier_text(&n.DeclaredIdentifier());
 | 
					                    *name = parser::identifier_text(&n.DeclaredIdentifier());
 | 
				
			||||||
                } else {
 | 
					                } else {
 | 
				
			||||||
| 
						 | 
					@ -1630,7 +1644,7 @@ pub fn type_from_node(
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        prop_type
 | 
					        prop_type
 | 
				
			||||||
    } else if let Some(object_node) = node.ObjectType() {
 | 
					    } else if let Some(object_node) = node.ObjectType() {
 | 
				
			||||||
        type_struct_from_node(object_node, diag, tr)
 | 
					        type_struct_from_node(object_node, diag, tr, None)
 | 
				
			||||||
    } else if let Some(array_node) = node.ArrayType() {
 | 
					    } else if let Some(array_node) = node.ArrayType() {
 | 
				
			||||||
        Type::Array(Box::new(type_from_node(array_node.Type(), diag, tr)))
 | 
					        Type::Array(Box::new(type_from_node(array_node.Type(), diag, tr)))
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
| 
						 | 
					@ -1644,6 +1658,7 @@ pub fn type_struct_from_node(
 | 
				
			||||||
    object_node: syntax_nodes::ObjectType,
 | 
					    object_node: syntax_nodes::ObjectType,
 | 
				
			||||||
    diag: &mut BuildDiagnostics,
 | 
					    diag: &mut BuildDiagnostics,
 | 
				
			||||||
    tr: &TypeRegister,
 | 
					    tr: &TypeRegister,
 | 
				
			||||||
 | 
					    rust_attributes: Option<Vec<String>>,
 | 
				
			||||||
) -> Type {
 | 
					) -> Type {
 | 
				
			||||||
    let fields = object_node
 | 
					    let fields = object_node
 | 
				
			||||||
        .ObjectTypeMember()
 | 
					        .ObjectTypeMember()
 | 
				
			||||||
| 
						 | 
					@ -1654,7 +1669,7 @@ pub fn type_struct_from_node(
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
        .collect();
 | 
					        .collect();
 | 
				
			||||||
    Type::Struct { fields, name: None, node: Some(object_node) }
 | 
					    Type::Struct { fields, name: None, node: Some(object_node), rust_attributes }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn animation_element_from_node(
 | 
					fn animation_element_from_node(
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -422,8 +422,9 @@ declare_syntax! {
 | 
				
			||||||
        /// `[ type ]`
 | 
					        /// `[ type ]`
 | 
				
			||||||
        ArrayType -> [ Type ],
 | 
					        ArrayType -> [ Type ],
 | 
				
			||||||
        /// `struct Foo := { ... }
 | 
					        /// `struct Foo := { ... }
 | 
				
			||||||
        StructDeclaration -> [DeclaredIdentifier, ObjectType],
 | 
					        StructDeclaration -> [DeclaredIdentifier, ObjectType, ?AtRustAttr],
 | 
				
			||||||
 | 
					        /// `@rust-attr(...)`
 | 
				
			||||||
 | 
					        AtRustAttr -> [],
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,6 +4,7 @@
 | 
				
			||||||
use super::element::{parse_element, parse_element_content};
 | 
					use super::element::{parse_element, parse_element_content};
 | 
				
			||||||
use super::prelude::*;
 | 
					use super::prelude::*;
 | 
				
			||||||
use super::r#type::parse_struct_declaration;
 | 
					use super::r#type::parse_struct_declaration;
 | 
				
			||||||
 | 
					use crate::parser::r#type::parse_rustattr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[cfg_attr(test, parser_test)]
 | 
					#[cfg_attr(test, parser_test)]
 | 
				
			||||||
/// ```test,Document
 | 
					/// ```test,Document
 | 
				
			||||||
| 
						 | 
					@ -45,6 +46,35 @@ pub fn parse_document(p: &mut impl Parser) -> bool {
 | 
				
			||||||
                    break;
 | 
					                    break;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					            "@" if p.nth(1).as_str() == "rust-attr" => {
 | 
				
			||||||
 | 
					                let mut is_export = false;
 | 
				
			||||||
 | 
					                let mut i = 0;
 | 
				
			||||||
 | 
					                loop {
 | 
				
			||||||
 | 
					                    let value = p.nth(i);
 | 
				
			||||||
 | 
					                    if value.as_str() == ")" && p.nth(i + 1).as_str() == "export" {
 | 
				
			||||||
 | 
					                        is_export = true;
 | 
				
			||||||
 | 
					                        break;
 | 
				
			||||||
 | 
					                    } else if (value.as_str() == ")"
 | 
				
			||||||
 | 
					                        && p.nth(i + 1).as_str() != "struct"
 | 
				
			||||||
 | 
					                        && p.nth(i + 1).as_str() != "export"
 | 
				
			||||||
 | 
					                        && p.nth(i + 1).as_str() != ")")
 | 
				
			||||||
 | 
					                        || (value.as_str() == ")" && p.nth(i + 1).as_str() == "struct")
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        break;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    i += 1;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                if is_export {
 | 
				
			||||||
 | 
					                    let mut p = p.start_node(SyntaxKind::ExportsList);
 | 
				
			||||||
 | 
					                    if !parse_rustattr(&mut *p) {
 | 
				
			||||||
 | 
					                        break;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    if !parse_rustattr(&mut *p) {
 | 
				
			||||||
 | 
					                        break;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
            _ => {
 | 
					            _ => {
 | 
				
			||||||
                if !parse_component(&mut *p) {
 | 
					                if !parse_component(&mut *p) {
 | 
				
			||||||
                    break;
 | 
					                    break;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -85,3 +85,57 @@ pub fn parse_struct_declaration(p: &mut impl Parser) -> bool {
 | 
				
			||||||
    parse_type_object(&mut *p);
 | 
					    parse_type_object(&mut *p);
 | 
				
			||||||
    true
 | 
					    true
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn parse_rustattr(p: &mut impl Parser) -> bool {
 | 
				
			||||||
 | 
					    let checkpoint = p.checkpoint();
 | 
				
			||||||
 | 
					    debug_assert_eq!(p.peek().as_str(), "@");
 | 
				
			||||||
 | 
					    p.consume(); // "@"
 | 
				
			||||||
 | 
					    if p.peek().as_str() != "rust-attr" {
 | 
				
			||||||
 | 
					        p.expect(SyntaxKind::AtRustAttr);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    p.consume(); // "rust-attr"
 | 
				
			||||||
 | 
					    p.expect(SyntaxKind::LParent);
 | 
				
			||||||
 | 
					    parse_parentheses(&mut *p);
 | 
				
			||||||
 | 
					    if p.peek().as_str() == "export" {
 | 
				
			||||||
 | 
					        p.consume();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    let mut p = p.start_node_at(checkpoint, SyntaxKind::StructDeclaration);
 | 
				
			||||||
 | 
					    p.consume(); // "struct"
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        let mut p = p.start_node(SyntaxKind::DeclaredIdentifier);
 | 
				
			||||||
 | 
					        p.expect(SyntaxKind::Identifier);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if p.peek().kind() == SyntaxKind::ColonEqual {
 | 
				
			||||||
 | 
					        p.warning("':=' to declare a struct is deprecated. Remove the ':='");
 | 
				
			||||||
 | 
					        p.consume();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    parse_type_object(&mut *p);
 | 
				
			||||||
 | 
					    true
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn parse_parentheses(p: &mut impl Parser) -> bool {
 | 
				
			||||||
 | 
					    let mut p = p.start_node(SyntaxKind::AtRustAttr);
 | 
				
			||||||
 | 
					    let mut opened = 0;
 | 
				
			||||||
 | 
					    let mut closed = 0;
 | 
				
			||||||
 | 
					    while closed <= opened {
 | 
				
			||||||
 | 
					        if p.peek().kind() == SyntaxKind::LParent {
 | 
				
			||||||
 | 
					            opened += 1;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if p.peek().kind() == SyntaxKind::RParent {
 | 
				
			||||||
 | 
					            closed += 1;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if closed == opened && opened != 0 && closed != 0 && p.peek().kind() != SyntaxKind::RParent
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            p.error("Parse error: `)` or `,`");
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        p.consume();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if p.peek().as_str() != "struct" && p.peek().as_str() != "export" {
 | 
				
			||||||
 | 
					        p.error("Parse error: expected `struct` or `export`");
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    true
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -139,6 +139,7 @@ fn compile_path_from_string_literal(
 | 
				
			||||||
        .collect(),
 | 
					        .collect(),
 | 
				
			||||||
        name: Some("Point".into()),
 | 
					        name: Some("Point".into()),
 | 
				
			||||||
        node: None,
 | 
					        node: None,
 | 
				
			||||||
 | 
					        rust_attributes: None,
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let mut points = Vec::new();
 | 
					    let mut points = Vec::new();
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -969,6 +969,7 @@ impl Expression {
 | 
				
			||||||
            fields: values.iter().map(|(k, v)| (k.clone(), v.ty())).collect(),
 | 
					            fields: values.iter().map(|(k, v)| (k.clone(), v.ty())).collect(),
 | 
				
			||||||
            name: None,
 | 
					            name: None,
 | 
				
			||||||
            node: None,
 | 
					            node: None,
 | 
				
			||||||
 | 
					            rust_attributes: None,
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
        Expression::Struct { ty, values }
 | 
					        Expression::Struct { ty, values }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					@ -1030,8 +1031,14 @@ impl Expression {
 | 
				
			||||||
                            fields: mut result_fields,
 | 
					                            fields: mut result_fields,
 | 
				
			||||||
                            name: result_name,
 | 
					                            name: result_name,
 | 
				
			||||||
                            node: result_node,
 | 
					                            node: result_node,
 | 
				
			||||||
 | 
					                            rust_attributes,
 | 
				
			||||||
 | 
					                        },
 | 
				
			||||||
 | 
					                        Type::Struct {
 | 
				
			||||||
 | 
					                            fields: elem_fields,
 | 
				
			||||||
 | 
					                            name: elem_name,
 | 
				
			||||||
 | 
					                            node: elem_node,
 | 
				
			||||||
 | 
					                            rust_attributes: deriven,
 | 
				
			||||||
                        },
 | 
					                        },
 | 
				
			||||||
                        Type::Struct { fields: elem_fields, name: elem_name, node: elem_node },
 | 
					 | 
				
			||||||
                    ) => {
 | 
					                    ) => {
 | 
				
			||||||
                        for (elem_name, elem_ty) in elem_fields.into_iter() {
 | 
					                        for (elem_name, elem_ty) in elem_fields.into_iter() {
 | 
				
			||||||
                            match result_fields.entry(elem_name) {
 | 
					                            match result_fields.entry(elem_name) {
 | 
				
			||||||
| 
						 | 
					@ -1052,6 +1059,7 @@ impl Expression {
 | 
				
			||||||
                            name: result_name.or(elem_name),
 | 
					                            name: result_name.or(elem_name),
 | 
				
			||||||
                            fields: result_fields,
 | 
					                            fields: result_fields,
 | 
				
			||||||
                            node: result_node.or(elem_node),
 | 
					                            node: result_node.or(elem_node),
 | 
				
			||||||
 | 
					                            rust_attributes: rust_attributes.or(deriven),
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    (Type::Color, Type::Brush) | (Type::Brush, Type::Color) => Type::Brush,
 | 
					                    (Type::Color, Type::Brush) | (Type::Brush, Type::Color) => Type::Brush,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -355,6 +355,7 @@ fn add_highlight_items(doc: &Document) {
 | 
				
			||||||
                        .collect(),
 | 
					                        .collect(),
 | 
				
			||||||
                    name: None,
 | 
					                    name: None,
 | 
				
			||||||
                    node: None,
 | 
					                    node: None,
 | 
				
			||||||
 | 
					                    rust_attributes: None,
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                .into(),
 | 
					                .into(),
 | 
				
			||||||
            ),
 | 
					            ),
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue