Switch to a pool of dynamic values for hot reloading (#2705)

* create the dynamic value pool

* assign ids to dynamic formatted segments

* separate the rendering and literal pools

* rsx output compiles again

* more examples compiling with new rsx expansion

* update template body explanation

* all workspace examples compile

* fix formatted segments in keys

* start hot reload diffing

* fix component literal hot reloading

* start integrate new hot reloading with the CLI

* simple hot reloads working

* Fix hot reloading blocks with components

* implement hot reloading for if chains

* Fix hot reloading after a template requires a full rebuild

* Fix hot reloading any attribute values

* remove unsafe from hot reload utils

* Fix hot reloading empty rsx

* add more hot reloading tests

* reorganize hot reload module

* fix web hydration

* fix empty rsx nodes in autoformatting

* fix tests

* remove path sorting logic from core

* make template names more consistent in debug mode

* fix quote_as_hot_reload_literal for explicitly typed literals

* fix can_be_shorthand for string literals

* fix formatted single dynamic expression

* Fix usize component properties and playwright tests

* remove default implementation for TemplateBody

* add a bunch more comments for diffing, scoring and why this scoring system is optimal
This commit is contained in:
Evan Almloff authored and GitHub committed 2024-07-30 17:16:27 +00:00
1 parent f3ca1484a1
commit 34bdcd15cf
40 files changed
+2288 -1897

No files matched your search

+8 -71
View File
@@ -1,6 +1,3 @@
#[cfg(feature = "hot_reload")]
use dioxus_core::internal::{FmtSegment, FmtedSegments};
use proc_macro2::{Span, TokenStream};
use quote::{quote, quote_spanned, ToTokens, TokenStreamExt};
use std::{collections::HashMap, str::FromStr};
@@ -94,64 +91,6 @@ impl IfmtInput {
map
}
#[cfg(feature = "hot_reload")]
pub fn fmt_segments(old: &Self, new: &Self) -> Option<FmtedSegments> {
use crate::intern;
// Make sure all the dynamic segments of b show up in a
for segment in new.segments.iter() {
if segment.is_formatted() && !old.segments.contains(segment) {
return None;
}
}
// Collect all the formatted segments from the original
let mut out = vec![];
// the original list of formatted segments
let mut fmted = old
.segments
.iter()
.flat_map(|f| match f {
crate::Segment::Literal(_) => None,
crate::Segment::Formatted(f) => Some(f),
})
.cloned()
.map(Some)
.collect::<Vec<_>>();
for segment in new.segments.iter() {
match segment {
crate::Segment::Literal(lit) => {
// create a &'static str by leaking the string
let lit = intern(lit.clone().into_boxed_str());
out.push(FmtSegment::Literal { value: lit });
}
crate::Segment::Formatted(fmt) => {
// Find the formatted segment in the original
// Set it to None when we find it so we don't re-render it on accident
let idx = fmted
.iter_mut()
.position(|_s| {
if let Some(s) = _s {
if s == fmt {
*_s = None;
return true;
}
}
false
})
.unwrap();
out.push(FmtSegment::Dynamic { id: idx });
}
}
}
Some(FmtedSegments::new(out))
}
fn is_simple_expr(&self) -> bool {
self.segments.iter().all(|seg| match seg {
Segment::Literal(_) => true,
@@ -272,6 +211,11 @@ impl IfmtInput {
impl ToTokens for IfmtInput {
fn to_tokens(&self, tokens: &mut TokenStream) {
// If the input is a string literal, we can just return it
if let Some(static_str) = self.to_static() {
return static_str.to_tokens(tokens);
}
// Try to turn it into a single _.to_string() call
if !cfg!(debug_assertions) {
if let Some(single_dynamic) = self.try_to_string() {
@@ -284,7 +228,7 @@ impl ToTokens for IfmtInput {
if self.is_simple_expr() {
let raw = &self.source;
tokens.extend(quote! {
::std::format_args!(#raw)
::std::format!(#raw)
});
return;
}
@@ -323,7 +267,7 @@ impl ToTokens for IfmtInput {
quote_spanned! {
span =>
::std::format_args!(
::std::format!(
#format_literal
#(, #positional_args)*
)
@@ -359,7 +303,7 @@ impl ToTokens for FormattedSegment {
let (fmt, seg) = (&self.format_args, &self.segment);
let fmt = format!("{{0:{fmt}}}");
tokens.append_all(quote! {
format_args!(#fmt, #seg)
format!(#fmt, #seg)
});
}
}
@@ -470,11 +414,4 @@ mod tests {
println!("{}", input.to_string_with_quotes());
assert!(input.is_static());
}
#[test]
fn fmt_segments() {
let left = syn::parse2::<IfmtInput>(quote! { "thing {abc}" }).unwrap();
let right = syn::parse2::<IfmtInput>(quote! { "thing" }).unwrap();
let _segments = IfmtInput::fmt_segments(&left, &right).unwrap();
}
}