View plugins

This commit is contained in:
2026-02-01 16:34:27 +01:00
parent d40a032bc1
commit b0eaa76398
3 changed files with 73 additions and 3 deletions
+7
View File
@@ -8,6 +8,7 @@ pub fn main() {
#[component] #[component]
fn App(cx: &Arc<Context>) -> View { fn App(cx: &Arc<Context>) -> View {
rsx! { rsx! {
impl my_center(11, 1)
Card { content: "Hello World".to_string() } Card { content: "Hello World".to_string() }
} }
.view(&cx) .view(&cx)
@@ -22,3 +23,9 @@ fn Card(cx: &Arc<Context>, content: String) -> View {
} }
.view(&cx) .view(&cx)
} }
fn my_center(ctx: &mut DrawContext, view: View, w: u16, h: u16) {
let (x, y) = ((ctx.area.width - w) / 2, (ctx.area.height - h) / 2);
let area = ctx.allocate(x, y, w, h);
ctx.draw_view(area, view);
}
+23
View File
@@ -51,6 +51,20 @@ fn emit_deps_vec(deps: &[Dep]) -> TokenStream {
} }
} }
/// Emits a Vec of plugins
fn emit_plugins(deps: &[ViewPlugin]) -> TokenStream {
deps.iter()
.map(|d| {
let path = &d.path;
if let Some(args) = &d.args {
quote!( #path(ctx, view #(,#args)*); )
} else {
quote!( #path(ctx, view); )
}
})
.collect()
}
/// Emits code for a single node within a scope /// Emits code for a single node within a scope
fn emit_node_scope(node: &RsxNode) -> TokenStream { fn emit_node_scope(node: &RsxNode) -> TokenStream {
match node { match node {
@@ -144,6 +158,7 @@ fn emit_node(node: &RsxNode) -> TokenStream {
} }
RsxNode::Component { RsxNode::Component {
plugins,
path, path,
props, props,
children, children,
@@ -171,9 +186,17 @@ fn emit_node(node: &RsxNode) -> TokenStream {
} }
}; };
if plugins.len() == 0 {
quote! { quote! {
scope.child(#component_expr, None); scope.child(#component_expr, None);
} }
} else {
let plugins_emit = emit_plugins(plugins);
quote! {
scope.child(#component_expr, Some(std::sync::Arc::new(|ctx, view| {#plugins_emit})));
}
}
} }
RsxNode::Mount(m) => quote! { RsxNode::Mount(m) => quote! {
+41 -1
View File
@@ -5,13 +5,14 @@
use std::collections::HashSet; use std::collections::HashSet;
use proc_macro2::Span; use proc_macro2::Span;
use syn::braced;
use syn::parse::discouraged::Speculative; use syn::parse::discouraged::Speculative;
use syn::punctuated::Punctuated;
use syn::{ use syn::{
Expr, Ident, LitStr, Pat, Path, Result, Token, Expr, Ident, LitStr, Pat, Path, Result, Token,
parse::{Parse, ParseStream}, parse::{Parse, ParseStream},
token::Brace, token::Brace,
}; };
use syn::{braced, parenthesized};
/// Root of an RSX expression /// Root of an RSX expression
pub struct RsxRoot { pub struct RsxRoot {
@@ -45,6 +46,8 @@ pub enum RsxNode {
Expr(Expr), Expr(Expr),
/// Component instantiation: `Component { prop: value, ... }` /// Component instantiation: `Component { prop: value, ... }`
Component { Component {
/// View plugins
plugins: Vec<ViewPlugin>,
/// Component path (e.g., `my_module::MyComponent`) /// Component path (e.g., `my_module::MyComponent`)
path: Path, path: Path,
/// Component properties /// Component properties
@@ -82,6 +85,11 @@ pub struct Dep {
pub is_dep: bool, pub is_dep: bool,
} }
pub struct ViewPlugin {
pub path: Path,
pub args: Option<Vec<Expr>>,
}
fn parse_deps(input: ParseStream) -> Result<Vec<Dep>> { fn parse_deps(input: ParseStream) -> Result<Vec<Dep>> {
let mut deps = Vec::new(); let mut deps = Vec::new();
@@ -122,6 +130,35 @@ fn parse_deps(input: ParseStream) -> Result<Vec<Dep>> {
Ok(deps) Ok(deps)
} }
fn parse_plugins(input: ParseStream) -> Result<Vec<ViewPlugin>> {
let mut plugins = Vec::new();
if input.peek(Token![impl]) {
input.parse::<Token![impl]>()?;
loop {
let path: Path = input.parse()?;
let args = if input.peek(syn::token::Paren) {
let content;
parenthesized!(content in input);
let exprs = Punctuated::<Expr, Token![,]>::parse_terminated(&content)?;
Some(exprs.into_iter().collect())
} else {
None
};
plugins.push(ViewPlugin { path, args });
if !input.peek(Token![,]) {
break;
}
input.parse::<Token![,]>()?;
}
}
Ok(plugins)
}
impl Parse for RsxNode { impl Parse for RsxNode {
fn parse(input: ParseStream) -> Result<Self> { fn parse(input: ParseStream) -> Result<Self> {
// @{ $expr } // @{ $expr }
@@ -201,10 +238,12 @@ impl Parse for RsxNode {
/// Parses Path or Path { ... } into RsxNode::Component. /// Parses Path or Path { ... } into RsxNode::Component.
fn parse_component_invocation(input: ParseStream) -> Result<RsxNode> { fn parse_component_invocation(input: ParseStream) -> Result<RsxNode> {
let plugins = parse_plugins(input)?;
let path: Path = input.parse()?; let path: Path = input.parse()?;
if !input.peek(Brace) { if !input.peek(Brace) {
return Ok(RsxNode::Component { return Ok(RsxNode::Component {
plugins,
path, path,
props: Vec::new(), props: Vec::new(),
children: Vec::new(), children: Vec::new(),
@@ -217,6 +256,7 @@ fn parse_component_invocation(input: ParseStream) -> Result<RsxNode> {
let (props, children) = parse_props_and_children(&content)?; let (props, children) = parse_props_and_children(&content)?;
Ok(RsxNode::Component { Ok(RsxNode::Component {
plugins,
path, path,
props, props,
children, children,