1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
//! Procedural macros for Whisker.
//!
//! - [`main`] — designates the user's app entry. Generates the
//! `whisker_app_main` and `whisker_tick` FFI exports the native
//! host calls into; the user writes `fn app() -> Element`.
//! - [`render!`] — fine-grained renderer macro. Emits imperative
//! `view::*` dispatch + `effect`s for dynamic parts. See
//! `crates/whisker-macros/src/render.rs` for the grammar.
//! - [`component`] — wraps a function so it runs inside a fresh
//! reactive owner. The owner is registered against the function's
//! fn pointer so the hot-reload remount path can find it. See
//! `docs/reactivity-design.md`.
use TokenStream;
use quote;
use ;
/// Annotates the user's app function (returning `whisker::Element`) and
/// generates the FFI symbols the iOS/Android host expects.
///
/// ```ignore
/// use whisker::prelude::*;
///
/// #[whisker::main]
/// fn app() -> Element {
/// render! { page { text(value: "Hello") } }
/// }
/// ```
///
/// Expands to (roughly):
///
/// ```ignore
/// fn app() -> Element { /* user body */ }
///
/// #[no_mangle]
/// pub extern "C" fn whisker_app_main(
/// engine: *mut std::ffi::c_void,
/// request_frame: Option<extern "C" fn(*mut std::ffi::c_void)>,
/// request_frame_data: *mut std::ffi::c_void,
/// ) {
/// ::whisker::__main_runtime::run(engine, request_frame, request_frame_data, app);
/// }
///
/// #[no_mangle]
/// pub extern "C" fn whisker_tick(engine: *mut std::ffi::c_void) -> bool {
/// ::whisker::__main_runtime::tick(engine)
/// }
/// ```
///
/// `request_frame` is the host's "wake up the render loop" callback. The
/// runtime invokes it when a signal update marks the tree dirty so the
/// host can unpause its `CADisplayLink` (or equivalent) to schedule the
/// next tick. Pass `None` to opt into an unconditional 60Hz loop.
///
/// `whisker_tick` returns `true` when the runtime is idle after the tick;
/// the host can pause its render loop until the next `request_frame`
/// fires.
/// Fine-grained renderer macro. Emits imperative element-creation
/// code that calls into [`whisker::runtime::view`] through the
/// thread-local installed renderer, and returns an [`Element`].
///
/// ```ignore
/// use whisker::prelude::*;
///
/// let handle = render! {
/// view(
/// style: "padding: 16px;",
/// on_tap: move |_| println!("tapped"),
/// ) {
/// text(value: "Hello, world")
/// }
/// };
/// ```
///
/// See `crates/whisker-macros/src/render.rs` for the full kwarg
/// grammar. Dynamic values flow through `Signal<T>` props; bare
/// `{expr}` blocks inside a children list are rejected (use
/// `text(value: <expr>)` instead).
/// `css!(name: value, …)` — kwarg syntax for the [`Css`] builder.
///
/// Lowers to a [`Css::new()`] method chain (`Css::new().name(value)
/// .…`). `Css` is taken from the call site's scope, so
/// `use whisker::prelude::*` (which re-exports `Css`) is the only
/// import callers need.
///
/// The proc-macro implementation tolerates partial input from
/// rust-analyzer's completion engine: a kwarg whose value hasn't
/// been typed yet (`css!(back|`) is expanded as
/// `.<name>(())` so RA still sees a real method-call site and
/// fires its method-name completion. The unit `()` is intentionally
/// type-incorrect; the program already doesn't compile while the
/// user is mid-typing.
///
/// ```ignore
/// use whisker::prelude::*;
///
/// let s = css!(
/// background_color: Color::hex(0x1A1330),
/// padding: (px(8), px(16)),
/// border: Border::new().width(px(1)).style(BorderStyle::Solid),
/// );
/// ```
///
/// [`Css`]: whisker_css::Css
/// [`Css::new()`]: whisker_css::Css::new
/// Mark a function as a Whisker reactive component.
///
/// The macro takes the user's `fn xxx(a: A, b: B) -> Element`
/// and emits both:
///
/// 1. A `XxxProps` struct (Pascal-cased function name + `Props`)
/// derived from the parameter list, plus a hand-rolled
/// `XxxPropsBuilder` so callers can construct Props via
/// `XxxProps::builder().a(...).b(...).build()`.
/// Each setter accepts `impl Into<T>` for `Into` coercion on the
/// call side (`&str` → `String`, `i32` → `f64`, …).
/// `Option<T>` props get a strip-option setter (accept the inner
/// `T`) and default to `None` when omitted. `Children` props get
/// a default empty closure. A `#[prop(default = expr)]` attribute
/// on a parameter inserts `expr` as the field's default at `.build()`.
/// Required fields that the user didn't set panic at `.build()` with
/// `"required field `xxx` was not set"`.
///
/// 2. A rewritten `fn xxx(__props: XxxProps) -> Element` whose
/// body destructures the props back into local variables and runs
/// the user's original `#block` inside the existing
/// `mount_component_remountable` machinery (per-component
/// remount + subsecond hot-reload integration).
///
/// The signature change is deliberate: positional `xxx(a, b)`
/// invocations no longer compile. User components are now invoked
/// exclusively through `render!`'s `xxx { a: …, b: … }` syntax,
/// which the `render!` macro lowers to
/// `xxx(XxxProps::builder().a(…).b(…).build())`. This unifies the
/// call-site shape with built-in elements (`view { … }`).
///
/// ```ignore
/// use whisker::prelude::*;
///
/// #[component]
/// fn counter(initial: i32) -> Element {
/// let (count, set_count) = signal(initial);
/// render! { /* ... */ }
/// }
///
/// // Call site (always through `render!`):
/// render! { counter { initial: 0 } }
/// ```
/// Declare a Whisker-side wrapper for a Lynx-registered view module's element.
///
/// ```ignore
/// #[whisker::module_component("Hello")]
/// pub fn hello(style: Signal<String>) -> Element;
/// ```
///
/// Generates the same Props + builder + PascalCase-alias surface as
/// `#[component]`, but the function body is **auto-generated**: it
/// calls `view::create_element_by_name(tag)` and then applies each
/// declared prop as either an inline-style (for the `style` prop) or
/// a SetAttribute (everything else, kebab-cased). Static vs reactive
/// dispatch goes through the same `apply_styles` / `apply_attr`
/// helpers built-in tags use, so a `Signal::Dynamic` prop transparently
/// effect-wraps the attribute write.
///
/// The tag string passed to Lynx at runtime is
/// `<cargo-crate-name>:<attr-tag>` — the macro auto-prepends
/// `env!("CARGO_PKG_NAME")` so two unrelated module packages can
/// both declare a component named `Hello` without colliding in
/// Lynx's behaviour registry. The matching platform-side
/// `@WhiskerModule` DSL `Name(...)` is namespaced the same way by
/// the per-platform codegen.
///
/// Imperative methods on a mounted element are dispatched through
/// the element's `ElementRef` (`ref:` prop) via
/// `ElementRef::invoke(method, args)` — there is no separate
/// element-method declaration macro.
///
/// Call-site shape mirrors built-in tags + user components:
///
/// ```ignore
/// render! {
/// Hello(style: "width: 100%; height: 8px;")
/// }
/// ```
///
/// See `crates/whisker-macros/src/module_component.rs` for the
/// emission details (children props are NOT yet supported; tracked
/// in follow-ups).