fix hot reloading rsx with nested rsx calls
This commit is contained in:
1 parent
21ca1599eb
commit
1530445972
5 files changed
+59
-10
No files matched your search
@@ -354,10 +354,42 @@ impl<'b> VirtualDom {
|
|||||||
id
|
id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Insert a new template into the VirtualDom's template registry
|
||||||
|
pub(crate) fn register_template_first_byte_index(&mut self, mut template: Template<'static>) {
|
||||||
|
// First, make sure we mark the template as seen, regardless if we process it
|
||||||
|
let (path, _) = template.name.rsplit_once(':').unwrap();
|
||||||
|
if let Some((_, old_template)) = self
|
||||||
|
.templates
|
||||||
|
.entry(path)
|
||||||
|
.or_default()
|
||||||
|
.iter_mut()
|
||||||
|
.min_by_key(|(byte_index, _)| **byte_index)
|
||||||
|
{
|
||||||
|
// the byte index of the hot reloaded template could be different
|
||||||
|
template.name = old_template.name;
|
||||||
|
*old_template = template;
|
||||||
|
} else {
|
||||||
|
panic!(
|
||||||
|
"Template {path} was not registered in\n{:#?}",
|
||||||
|
self.templates
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If it's all dynamic nodes, then we don't need to register it
|
||||||
|
if !template.is_completely_dynamic() {
|
||||||
|
self.mutations.templates.push(template);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Insert a new template into the VirtualDom's template registry
|
/// Insert a new template into the VirtualDom's template registry
|
||||||
pub(crate) fn register_template(&mut self, template: Template<'static>) {
|
pub(crate) fn register_template(&mut self, template: Template<'static>) {
|
||||||
// First, make sure we mark the template as seen, regardless if we process it
|
// First, make sure we mark the template as seen, regardless if we process it
|
||||||
self.templates.insert(template.name, template);
|
let (path, byte_index) = template.name.rsplit_once(':').unwrap();
|
||||||
|
let byte_index = byte_index.parse::<usize>().unwrap();
|
||||||
|
self.templates
|
||||||
|
.entry(path)
|
||||||
|
.or_default()
|
||||||
|
.insert(byte_index, template);
|
||||||
|
|
||||||
// If it's all dynamic nodes, then we don't need to register it
|
// If it's all dynamic nodes, then we don't need to register it
|
||||||
if !template.is_completely_dynamic() {
|
if !template.is_completely_dynamic() {
|
||||||
|
|||||||
@@ -57,11 +57,17 @@ impl<'b> VirtualDom {
|
|||||||
fn diff_node(&mut self, left_template: &'b VNode<'b>, right_template: &'b VNode<'b>) {
|
fn diff_node(&mut self, left_template: &'b VNode<'b>, right_template: &'b VNode<'b>) {
|
||||||
// If hot reloading is enabled, we need to make sure we're using the latest template
|
// If hot reloading is enabled, we need to make sure we're using the latest template
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
if let Some(template) = self.templates.get(right_template.template.get().name) {
|
{
|
||||||
if *template != right_template.template.get() {
|
let (path, byte_index) = right_template.template.get().name.rsplit_once(':').unwrap();
|
||||||
right_template.template.set(*template);
|
if let Some(map) = self.templates.get(path) {
|
||||||
if *template != left_template.template.get() {
|
let byte_index = byte_index.parse::<usize>().unwrap();
|
||||||
return self.replace(left_template, [right_template]);
|
if let Some(&template) = map.get(&byte_index) {
|
||||||
|
if template != right_template.template.get() {
|
||||||
|
right_template.template.set(template);
|
||||||
|
if template != left_template.template.get() {
|
||||||
|
return self.replace(left_template, [right_template]);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -142,7 +142,8 @@ use std::{any::Any, borrow::BorrowMut, cell::Cell, collections::BTreeSet, future
|
|||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
pub struct VirtualDom {
|
pub struct VirtualDom {
|
||||||
pub(crate) templates: FxHashMap<TemplateId, Template<'static>>,
|
// Maps a template path to a map of byteindexes to templates
|
||||||
|
pub(crate) templates: FxHashMap<TemplateId, FxHashMap<usize, Template<'static>>>,
|
||||||
pub(crate) scopes: Slab<Box<ScopeState>>,
|
pub(crate) scopes: Slab<Box<ScopeState>>,
|
||||||
pub(crate) dirty_scopes: BTreeSet<DirtyScope>,
|
pub(crate) dirty_scopes: BTreeSet<DirtyScope>,
|
||||||
pub(crate) scheduler: Rc<Scheduler>,
|
pub(crate) scheduler: Rc<Scheduler>,
|
||||||
@@ -459,12 +460,16 @@ impl VirtualDom {
|
|||||||
/// This is the primitive that enables hot-reloading.
|
/// This is the primitive that enables hot-reloading.
|
||||||
///
|
///
|
||||||
/// The caller must ensure that the template refrences the same dynamic attributes and nodes as the original template.
|
/// The caller must ensure that the template refrences the same dynamic attributes and nodes as the original template.
|
||||||
|
///
|
||||||
|
/// This will only replace the the parent template, not any nested templates.
|
||||||
pub fn replace_template(&mut self, template: Template<'static>) {
|
pub fn replace_template(&mut self, template: Template<'static>) {
|
||||||
self.register_template(template);
|
self.register_template_first_byte_index(template);
|
||||||
// iterating a slab is very inefficient, but this is a rare operation that will only happen during development so it's fine
|
// iterating a slab is very inefficient, but this is a rare operation that will only happen during development so it's fine
|
||||||
for (_, scope) in &self.scopes {
|
for (_, scope) in &self.scopes {
|
||||||
if let Some(RenderReturn::Sync(Some(sync))) = scope.try_root_node() {
|
if let Some(RenderReturn::Sync(Some(sync))) = scope.try_root_node() {
|
||||||
if sync.template.get().name == template.name {
|
if sync.template.get().name.rsplit_once(':').unwrap().0
|
||||||
|
== template.name.rsplit_once(':').unwrap().0
|
||||||
|
{
|
||||||
let height = scope.height;
|
let height = scope.height;
|
||||||
self.dirty_scopes.insert(DirtyScope {
|
self.dirty_scopes.insert(DirtyScope {
|
||||||
height,
|
height,
|
||||||
@@ -473,6 +478,7 @@ impl VirtualDom {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
assert!(!self.dirty_scopes.is_empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Performs a *full* rebuild of the virtual dom, returning every edit required to generate the actual dom from scratch.
|
/// Performs a *full* rebuild of the virtual dom, returning every edit required to generate the actual dom from scratch.
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ pub struct IfmtInput {
|
|||||||
|
|
||||||
impl IfmtInput {
|
impl IfmtInput {
|
||||||
pub fn is_static(&self) -> bool {
|
pub fn is_static(&self) -> bool {
|
||||||
matches!(self.segments.as_slice(), &[Segment::Literal(_)])
|
matches!(self.segments.as_slice(), &[Segment::Literal(_)] | &[])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -187,6 +187,9 @@ impl<'a, Ctx: HotReloadingContext> ToTokens for TemplateRenderer<'a, Ctx> {
|
|||||||
out
|
out
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let spndbg = format!("{:?}", self.roots[0].span());
|
||||||
|
let root_col = spndbg[9..].split("..").next().unwrap();
|
||||||
|
|
||||||
// Render and release the mutable borrow on context
|
// Render and release the mutable borrow on context
|
||||||
let roots = quote! { #( #root_printer ),* };
|
let roots = quote! { #( #root_printer ),* };
|
||||||
let node_printer = &context.dynamic_nodes;
|
let node_printer = &context.dynamic_nodes;
|
||||||
@@ -202,6 +205,8 @@ impl<'a, Ctx: HotReloadingContext> ToTokens for TemplateRenderer<'a, Ctx> {
|
|||||||
line!(),
|
line!(),
|
||||||
":",
|
":",
|
||||||
column!(),
|
column!(),
|
||||||
|
":",
|
||||||
|
#root_col
|
||||||
),
|
),
|
||||||
roots: &[ #roots ],
|
roots: &[ #roots ],
|
||||||
node_paths: &[ #(#node_paths),* ],
|
node_paths: &[ #(#node_paths),* ],
|
||||||
|
|||||||
Reference in new issue
Block a user