zng/hot_reload.rs
1//! Hot reloading instrumentation macros and service.
2//!
3//! Hot reloading rebuilds an instrumented library and automatically re-inits widgets that
4//! are using marked nodes, properties, all without needing to restart the application.
5//!
6//! This feature is very useful when developing something that requires interactive feedback adjustments, but
7//! is does require some setup.
8//!
9//! # Setup
10//!
11//! First your project must be split into two crates, a binary and a library. The binary crate runs the app like normal
12//! it depends on the library crate and `zng` with `"hot_reload"` feature. The library crate is the one that will be
13//! instrumented for hot reloading.
14//!
15//! First in the `Cargo.toml` for the library crate add:
16//!
17//! ```toml
18//! [lib]
19//! crate-type = ["lib", "cdylib"]
20//! ```
21//!
22//! Then in the library crate `src/lib.rs` root add a call to the [`zng_hot_entry!`] item macro:
23//!
24//! ```
25//! zng::hot_reload::zng_hot_entry!();
26//! ```
27//!
28//! Then set the [`hot_node`] attribute in node or property functions that you are developing:
29//!
30//! ```
31//! use zng::{prelude::*, prelude_wgt::*};
32//! # zng::hot_reload::zng_hot_entry!();
33//!
34//! #[hot_node]
35//! pub fn hello_text(input: impl IntoVar<bool>) -> impl UiNode {
36//! let input = input.into_var();
37//! Text! {
38//! txt = greeting_text();
39//! widget::background_color = rgb(0, 100, 0);
40//! when *#{input} {
41//! font_weight = FontWeight::BOLD;
42//! }
43//! }
44//! }
45//!
46//! fn greeting_text() -> Txt {
47//! "Hello!".into()
48//! }
49//!
50//! fn other_ui() -> impl UiNode {
51//! Container! {
52//! child = hello_text(true);
53//! text::font_size = 2.em();
54//! }
55//! }
56//! # fn main() { }
57//! ```
58//!
59//! In the example above the `hello_text` function is marked for hot reload, any change in the library crate
60//! will trigger a rebuild and widget reinit.
61//!
62//! In the example you can change anything except the signature of `hello_text`, changes inside the function or
63//! inside any other item used by the function will hot reload, you can add or remove properties, replace
64//! the `Text!` widget with some other node type, even add Cargo dependencies and use their items.
65//!
66//! Changes in other *cold nodes* that only contextually affect the hot node will trigger a hot reload,
67//! **but will not affect** the hot node, in the example the `font_size` set in `other_ui` affects the
68//! hot node even after reload, but the value is fixed at `2.em()`, if you change it the changes are ignored.
69//!
70//! # How It Works
71//!
72//! On app init, if at least one `#[hot_node]` is set, all the library crate files are monitored for changes, any change triggers a
73//! background rebuild, when the rebuild is finished all `#[hot_node]` functions or properties reinit the related widget,
74//! on reinit the new compiled code will run.
75//!
76//! ## Limitations
77//!
78//! There are some limitations to the current implementation:
79//!
80//! ##### Only Node Functions
81//!
82//! Currently this is only implemented for node functions, this covers all property nodes, intrinsic nodes and functions like
83//! in the example above that instantiate widgets, but the widget type must implement `UiNode`, widgets that build different types
84//! cannot be hot reloaded, because of this the `Window!` widget cannot be hot reloaded.
85//!
86//! ##### Limited Function Signature
87//!
88//! Some input types are not supported for the hot node function. Only the `impl` generics supported by [`property`] and
89//! types that are `Clone + Any + Send` are supported. A compile time error is generated if you attempt to use an invalid function
90//! signature. Only the output type `impl UiNode` is supported. Generic properties (named generic params) are also not supported.
91//!
92//! ##### Rebuild Speed
93//!
94//! The rebuild speed is only as fast as Rust incremental compilation, it should be pretty fast for small changes,
95//! but if your library crate grows large you might want to create a separate *design library* where you place
96//! only the nodes under current interactive development.
97//!
98//! You need to make sure that the same Cargo feature set is used to rebuild, by default the command is `cargo build`, interrupted
99//! as soon as the hot library rebuilds. The build command can be overridden using [`HOT_RELOAD.rebuilder`], see the method
100//! documentation for more details.
101//!
102//! [`HOT_RELOAD.rebuilder`]: HOT_RELOAD::rebuilder
103//!
104//! ##### Rebuild Races IDE for Target Lock
105//!
106//! The rebuild uses the same target directory used by `cargo check/clippy`, this means that if your IDE (Rust Analyzer) runs
107//! these checks it will race the hot reload rebuild process to acquire the exclusive lock to the target dir. If you are seeing
108//! this interference try pausing your IDE analyzer before running.
109//!
110//! ##### Any Change Reloads All Hot Nodes
111//!
112//! Any change on the crate triggers a rebuild and all hot nodes reinit because of it. You can set `#[hot_node]` on multiple functions
113//! at a time, but this will cause large parts of the screen to reload. It is recommenced that you only set it on functions
114//! under iterative development.
115//!
116//! Hot node reinit reloads the entire tree branch, so descendants of hot nodes are reinited too. This may cause some state to be lost,
117//! in particular all state inited inside the hot node will be reinited.
118//!
119//! ##### Hot Libraries Don't Unload
120//!
121//! Every hot reload represents a new version of the library reloading and the previous one cannot be unloaded because static references
122//! to it might persist in the program, usually `&'static str` texts captured during info rebuild.
123//!
124//! [`property`]: crate::widget::property#input-types
125//!
126//! # Full API
127//!
128//! See [`zng_ext_hot_reload`] for the full hot reload API.
129
130/// Expands an UI node function into a hot reloading one.
131///
132/// See the [module] level documentation for more details about hot reloading.
133///
134/// [module]: crate::hot_reload
135///
136/// # Attribute
137///
138/// This attribute has one optional argument, a string literal that uniquely identifies the function among all other
139/// hot node functions. The default name is only the function name, so you can use this argument to resolve name conflicts.
140///
141/// # Limitations
142///
143/// This attribute only accepts inputs with a single name, no destructuring, and of type that is `Clone + Any + Send` or
144/// the `impl` generics supported by [`property`]. Unlike property this function does not support named generic parameters.
145///
146/// The function output type must be `impl UiNode`, the attribute will change the internal node type.
147///
148/// [`property`]: crate::widget::property#input-types
149#[cfg(hot_reload)]
150pub use zng_ext_hot_reload::hot_node;
151
152/// Declare the dynamic library hot reload entry.
153///
154/// This must be called at the root (`src/lib.rs`) of the library that will hot reload. See the [module] level
155/// documentation for more details.
156///
157/// [module]: crate::hot_reload
158#[cfg(hot_reload)]
159pub use zng_ext_hot_reload::zng_hot_entry;
160
161#[cfg(hot_reload)]
162pub use zng_ext_hot_reload::{BuildArgs, BuildError, HOT_RELOAD};
163
164pub use zng_unique_id::{hot_static, hot_static_ref, lazy_static, lazy_static_init};