1use std::{
2 any::{
3 Any,
4 TypeId,
5 },
6 cell::RefCell,
7 cmp::Ordering,
8 collections::VecDeque,
9 fmt::Debug,
10 rc::Rc,
11 sync::atomic::AtomicU64,
12};
13
14use futures_lite::{
15 FutureExt,
16 StreamExt,
17};
18use itertools::Itertools;
19use pathgraph::PathGraph;
20use rustc_hash::{
21 FxHashMap,
22 FxHashSet,
23};
24use tiny_diff::DiffNode;
25
26use crate::{
27 current_context::CurrentContext,
28 diff_key::DiffKey,
29 element::{
30 Element,
31 ElementExt,
32 EventHandlerType,
33 },
34 events::{
35 data::{
36 Event,
37 EventType,
38 },
39 name::EventName,
40 },
41 node_id::NodeId,
42 path_element::PathElement,
43 prelude::{
44 Task,
45 TaskId,
46 },
47 reactive_context::ReactiveContext,
48 scope::{
49 PathNode,
50 Scope,
51 ScopeStorage,
52 },
53 scope_id::ScopeId,
54 tree::DiffModifies,
55};
56
57#[derive(Debug, PartialEq, Eq)]
58pub enum MutationRemove {
59 Element { id: NodeId, index: u32 },
61 Scope { id: NodeId },
63}
64
65impl PartialOrd for MutationRemove {
66 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
67 Some(self.cmp(other))
68 }
69}
70
71impl Ord for MutationRemove {
72 fn cmp(&self, other: &Self) -> Ordering {
73 use MutationRemove::*;
74 match (self, other) {
75 (Element { index: a, .. }, Element { index: b, .. }) => b.cmp(a),
77 (Element { .. }, Scope { .. }) => Ordering::Less,
79 (Scope { .. }, Element { .. }) => Ordering::Greater,
80 (Scope { id: a }, Scope { id: b }) => b.cmp(a),
82 }
83 }
84}
85
86impl MutationRemove {
87 pub fn node_id(&self) -> NodeId {
88 match self {
89 Self::Element { id, .. } => *id,
90 Self::Scope { id } => *id,
91 }
92 }
93}
94
95pub struct MutationAdd {
96 pub node_id: NodeId,
97 pub parent_id: NodeId,
98 pub index: u32,
99 pub element: Rc<dyn ElementExt>,
100}
101
102pub struct MutationModified {
103 pub node_id: NodeId,
104 pub element: Rc<dyn ElementExt>,
105 pub flags: DiffModifies,
106}
107
108#[derive(Default)]
109pub struct Mutations {
110 pub added: Vec<MutationAdd>,
112 pub modified: Vec<MutationModified>,
114 pub removed: Vec<MutationRemove>,
116 pub moved: FxHashMap<NodeId, Vec<NodeId>>,
118}
119
120impl Mutations {
121 pub fn is_empty(&self) -> bool {
122 self.added.is_empty()
123 && self.modified.is_empty()
124 && self.removed.is_empty()
125 && self.moved.is_empty()
126 }
127}
128
129impl Debug for Mutations {
130 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
131 f.write_fmt(format_args!(
132 "Added: {:?} Modified: {:?} Removed: {:?} Moved: {:?}",
133 self.added
134 .iter()
135 .map(|m| (m.node_id, m.parent_id, m.index))
136 .collect::<Vec<_>>(),
137 self.modified.iter().map(|m| m.node_id).collect::<Vec<_>>(),
138 self.removed,
139 self.moved
140 ))
141 }
142}
143
144pub enum Message {
145 MarkScopeAsDirty(ScopeId),
146 PollTask(TaskId),
147}
148
149#[derive(Clone, Copy, PartialEq, Eq, Debug)]
151pub enum TasksPollStage {
152 Started,
153 Finished,
154}
155
156type AdditionParents = FxHashMap<Box<[u32]>, (NodeId, Option<Vec<u32>>)>;
158
159type ReorderedParents = FxHashMap<NodeId, Box<[u32]>>;
160
161pub struct Runner {
162 pub scopes: FxHashMap<ScopeId, Rc<RefCell<Scope>>>,
163 pub scopes_storages: Rc<RefCell<FxHashMap<ScopeId, ScopeStorage>>>,
164
165 pub(crate) dirty_scopes: FxHashSet<ScopeId>,
166 pub(crate) dirty_tasks: VecDeque<TaskId>,
167
168 pub node_to_scope: FxHashMap<NodeId, ScopeId>,
169
170 pub(crate) node_id_counter: NodeId,
171 pub(crate) scope_id_counter: ScopeId,
172 pub(crate) task_id_counter: Rc<AtomicU64>,
173
174 pub(crate) tasks: Rc<RefCell<FxHashMap<TaskId, Rc<RefCell<Task>>>>>,
175
176 pub(crate) sender: futures_channel::mpsc::UnboundedSender<Message>,
177 pub(crate) receiver: futures_channel::mpsc::UnboundedReceiver<Message>,
178}
179
180impl Debug for Runner {
181 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
182 f.debug_struct("Runner")
183 .field("dirty_scopes", &self.dirty_scopes.len())
184 .field("dirty_tasks", &self.dirty_tasks.len())
185 .field("node_to_scope", &self.node_to_scope.len())
186 .field("scopes", &self.scopes.len())
187 .field("scopes_storages", &self.scopes_storages.borrow().len())
188 .field("tasks", &self.tasks.borrow().len())
189 .finish()
190 }
191}
192
193impl Drop for Runner {
194 fn drop(&mut self) {
195 for (scope_id, _scope) in self
197 .scopes
198 .drain()
199 .sorted_by_key(|s| s.1.borrow().height)
200 .rev()
201 {
202 CurrentContext::run_with_reactive(self.create_context(scope_id), || {
203 let mut removed_tasks = Vec::new();
204
205 self.tasks.borrow_mut().retain(|task_id, task| {
206 if task.borrow().scope_id == scope_id {
207 removed_tasks.push((*task_id, task.clone()));
208 false
209 } else {
210 true
211 }
212 });
213 drop(removed_tasks);
214 let _scope = self.scopes_storages.borrow_mut().remove(&scope_id);
215 });
216 }
217 }
218}
219
220impl Runner {
221 pub fn new(root: impl Fn() -> Element + 'static) -> Self {
222 let (sender, receiver) = futures_channel::mpsc::unbounded::<Message>();
223 Self {
224 scopes: FxHashMap::from_iter([(
225 ScopeId::ROOT,
226 Rc::new(RefCell::new(Scope {
227 parent_node_id_in_parent: NodeId::ROOT,
228 path_in_parent: Box::from([]),
229 height: 0,
230 parent_id: None,
231 id: ScopeId::ROOT,
232 key: DiffKey::Root,
233 comp: Rc::new(move |_| root()),
234 props: Rc::new(()),
235 element: None,
236 nodes: {
237 let mut map = PathGraph::new();
238 map.insert(
239 &[],
240 PathNode {
241 node_id: NodeId::ROOT,
242 scope_id: None,
243 },
244 );
245 map
246 },
247 })),
248 )]),
249 scopes_storages: Rc::new(RefCell::new(FxHashMap::from_iter([(
250 ScopeId::ROOT,
251 ScopeStorage::new(None, |writer| {
252 ReactiveContext::new_for_scope(sender.clone(), ScopeId::ROOT, writer)
253 }),
254 )]))),
255
256 node_to_scope: FxHashMap::from_iter([(NodeId::ROOT, ScopeId::ROOT)]),
257
258 node_id_counter: NodeId::ROOT,
259 scope_id_counter: ScopeId::ROOT,
260 task_id_counter: Rc::default(),
261
262 dirty_tasks: VecDeque::default(),
263 dirty_scopes: FxHashSet::from_iter([ScopeId::ROOT]),
264
265 tasks: Rc::default(),
266
267 sender,
268 receiver,
269 }
270 }
271
272 #[cfg(all(debug_assertions, feature = "debug-integrity"))]
273 #[cfg_attr(feature = "hotpath", hotpath::measure)]
274 pub fn verify_scopes_integrity(&self) {
275 let mut visited = FxHashSet::default();
276 let size = self.scopes.len();
277 let mut buffer = vec![ScopeId::ROOT];
278 while let Some(scope_id) = buffer.pop() {
279 if visited.contains(&scope_id) {
280 continue;
281 }
282 visited.insert(scope_id);
283 let scope = self.scopes.get(&scope_id).unwrap();
284 let scope = scope.borrow();
285 if let Some(parent) = scope.parent_id {
286 buffer.push(parent);
287 }
288 scope.nodes.traverse(&[], |_, &PathNode { scope_id, .. }| {
289 if let Some(scope_id) = scope_id {
290 buffer.push(scope_id);
291 }
292 });
293 }
294 assert_eq!(size, visited.len())
295 }
296
297 fn create_context(&self, scope_id: ScopeId) -> CurrentContext {
298 CurrentContext {
299 scope_id,
300 scopes_storages: self.scopes_storages.clone(),
301 tasks: self.tasks.clone(),
302 task_id_counter: self.task_id_counter.clone(),
303 sender: self.sender.clone(),
304 }
305 }
306
307 pub fn with_root_context<T>(&self, run: impl FnOnce() -> T) -> T {
308 self.run_in(run)
309 }
310
311 pub fn provide_root_context<T: 'static + Clone>(&mut self, context: impl FnOnce() -> T) -> T {
312 self.run_in(|| {
313 let context = context();
314 let mut scopes_storages = self.scopes_storages.borrow_mut();
315 let root_scope_storage = scopes_storages.get_mut(&ScopeId::ROOT).unwrap();
316 root_scope_storage
317 .contexts
318 .insert(TypeId::of::<T>(), Rc::new(context.clone()));
319
320 context
321 })
322 }
323
324 pub fn handle_event(
327 &mut self,
328 node_id: impl Into<NodeId>,
329 event_name: EventName,
330 event_type: EventType,
331 bubbles: bool,
332 ) -> bool {
333 let node_id = node_id.into();
334 #[cfg(debug_assertions)]
335 tracing::info!("Handling event {event_name:?} for {node_id:?}");
336 let propagate = Rc::new(RefCell::new(bubbles));
337 let default = Rc::new(RefCell::new(true));
338
339 let Some(scope_id) = self.node_to_scope.get(&node_id) else {
340 return true;
341 };
342 let Some(path) = self
343 .scopes
344 .get(scope_id)
345 .unwrap()
346 .borrow()
347 .nodes
348 .find_path(|value| {
349 value
350 == Some(&PathNode {
351 node_id,
352 scope_id: None,
353 })
354 })
355 else {
356 return true;
357 };
358
359 let mut current_target = Some((path, *scope_id));
360 while let Some((path, scope_id)) = current_target.take() {
361 let scope = self.scopes.get(&scope_id).cloned().unwrap();
362 scope.borrow().with_element(&path, |element| {
363 match element {
364 PathElement::Component { .. } => {
365 unreachable!()
366 }
367 PathElement::Element { element, .. } => {
368 CurrentContext::run(self.create_context(scope_id), || {
369 match &event_type {
370 EventType::AccessibilityAction(data) => {
371 if let Some(event_handlers) = element.events_handlers()
372 && let Some(EventHandlerType::AccessibilityAction(handler)) =
373 event_handlers.get(&event_name)
374 {
375 handler.call(Event {
376 data: data.clone(),
377 propagate: propagate.clone(),
378 default: default.clone(),
379 });
380 }
381 }
382 EventType::Mouse(data) => {
383 let event_handlers = element.events_handlers();
384 if let Some(event_handlers) = event_handlers {
385 match event_handlers.get(&event_name) {
386 Some(EventHandlerType::Mouse(handler)) => {
387 handler.call(Event {
388 data: data.clone(),
389 propagate: propagate.clone(),
390 default: default.clone(),
391 });
392 }
393 Some(_) => unreachable!(),
394 _ => {}
395 }
396 }
397 }
398 EventType::Keyboard(data) => {
399 let event_handlers = element.events_handlers();
400 if let Some(event_handlers) = event_handlers {
401 match event_handlers.get(&event_name) {
402 Some(EventHandlerType::Keyboard(handler)) => {
403 handler.call(Event {
404 data: data.clone(),
405 propagate: propagate.clone(),
406 default: default.clone(),
407 });
408 }
409 Some(_) => unreachable!(),
410 _ => {}
411 }
412 }
413 }
414 EventType::Sized(data) => {
415 let event_handlers = element.events_handlers();
416 if let Some(event_handlers) = event_handlers {
417 match event_handlers.get(&event_name) {
418 Some(EventHandlerType::Sized(handler)) => {
419 handler.call(Event {
420 data: data.clone(),
421 propagate: propagate.clone(),
422 default: default.clone(),
423 });
424 }
425 Some(_) => unreachable!(),
426 _ => {}
427 }
428 }
429 }
430 EventType::Visible(data) => {
431 let event_handlers = element.events_handlers();
432 if let Some(event_handlers) = event_handlers {
433 match event_handlers.get(&event_name) {
434 Some(EventHandlerType::Visible(handler)) => {
435 handler.call(Event {
436 data: data.clone(),
437 propagate: propagate.clone(),
438 default: default.clone(),
439 });
440 }
441 Some(_) => unreachable!(),
442 _ => {}
443 }
444 }
445 }
446 EventType::Styled(data) => {
447 let event_handlers = element.events_handlers();
448 if let Some(event_handlers) = event_handlers {
449 match event_handlers.get(&event_name) {
450 Some(EventHandlerType::Styled(handler)) => {
451 handler.call(Event {
452 data: data.clone(),
453 propagate: propagate.clone(),
454 default: default.clone(),
455 });
456 }
457 Some(_) => unreachable!(),
458 _ => {}
459 }
460 }
461 }
462 EventType::Wheel(data) => {
463 let event_handlers = element.events_handlers();
464 if let Some(event_handlers) = event_handlers {
465 match event_handlers.get(&event_name) {
466 Some(EventHandlerType::Wheel(handler)) => {
467 handler.call(Event {
468 data: data.clone(),
469 propagate: propagate.clone(),
470 default: default.clone(),
471 });
472 }
473 Some(_) => unreachable!(),
474 _ => {}
475 }
476 }
477 }
478 EventType::Touch(data) => {
479 let event_handlers = element.events_handlers();
480 if let Some(event_handlers) = event_handlers {
481 match event_handlers.get(&event_name) {
482 Some(EventHandlerType::Touch(handler)) => {
483 handler.call(Event {
484 data: data.clone(),
485 propagate: propagate.clone(),
486 default: default.clone(),
487 });
488 }
489 Some(_) => unreachable!(),
490 _ => {}
491 }
492 }
493 }
494 EventType::Pointer(data) => {
495 let event_handlers = element.events_handlers();
496 if let Some(event_handlers) = event_handlers {
497 match event_handlers.get(&event_name) {
498 Some(EventHandlerType::Pointer(handler)) => {
499 handler.call(Event {
500 data: data.clone(),
501 propagate: propagate.clone(),
502 default: default.clone(),
503 });
504 }
505 Some(_) => unreachable!(),
506 _ => {}
507 }
508 }
509 }
510 EventType::File(data) => {
511 let event_handlers = element.events_handlers();
512 if let Some(event_handlers) = event_handlers {
513 match event_handlers.get(&event_name) {
514 Some(EventHandlerType::File(handler)) => {
515 handler.call(Event {
516 data: data.clone(),
517 propagate: propagate.clone(),
518 default: default.clone(),
519 });
520 }
521 Some(_) => unreachable!(),
522 _ => {}
523 }
524 }
525 }
526 EventType::ImePreedit(data) => {
527 let event_handlers = element.events_handlers();
528 if let Some(event_handlers) = event_handlers {
529 match event_handlers.get(&event_name) {
530 Some(EventHandlerType::ImePreedit(handler)) => {
531 handler.call(Event {
532 data: data.clone(),
533 propagate: propagate.clone(),
534 default: default.clone(),
535 });
536 }
537 Some(_) => unreachable!(),
538 _ => {}
539 }
540 }
541 }
542 }
543
544 if *propagate.borrow() {
546 if path.len() > 1 {
547 current_target
549 .replace((path[..path.len() - 1].to_vec(), scope_id));
550 } else {
551 let mut parent_scope_id = scope.borrow().parent_id;
552 loop {
554 if let Some(parent_id) = parent_scope_id.take() {
555 let parent_scope = self.scopes.get(&parent_id).unwrap();
556 let path =
557 parent_scope.borrow().nodes.find_path(|value| {
558 value
559 == Some(&PathNode {
560 node_id: scope
561 .borrow()
562 .parent_node_id_in_parent,
563 scope_id: None,
564 })
565 });
566 if let Some(path) = path {
567 current_target.replace((path, parent_id));
568 break;
569 } else {
570 parent_scope_id = parent_scope.borrow().parent_id;
571 }
572 } else {
573 return;
574 }
575 }
576 }
577 }
578 })
579 }
580 }
581 });
582 }
583 *default.borrow()
584 }
585
586 #[cfg_attr(feature = "hotpath", hotpath::measure)]
587 pub async fn handle_events(&mut self) {
588 self.handle_events_with(&mut |_| {}).await
589 }
590
591 pub async fn handle_events_with(&mut self, observer: &mut dyn FnMut(TasksPollStage)) {
593 loop {
594 while let Ok(msg) = self.receiver.try_recv() {
595 match msg {
596 Message::MarkScopeAsDirty(scope_id) => {
597 self.dirty_scopes.insert(scope_id);
598 }
599 Message::PollTask(task_id) => {
600 self.dirty_tasks.push_back(task_id);
601 }
602 }
603 }
604
605 if !self.dirty_scopes.is_empty() {
606 return;
607 }
608
609 self.poll_dirty_tasks(observer);
610
611 if !self.dirty_scopes.is_empty() {
612 return;
613 }
614
615 let Some(msg) = self.receiver.next().await else {
616 return;
617 };
618 match msg {
619 Message::MarkScopeAsDirty(scope_id) => {
620 self.dirty_scopes.insert(scope_id);
621 }
622 Message::PollTask(task_id) => {
623 self.dirty_tasks.push_back(task_id);
624 }
625 }
626 }
627 }
628
629 #[cfg_attr(feature = "hotpath", hotpath::measure)]
631 pub fn handle_events_immediately(&mut self) {
632 self.handle_events_immediately_with(&mut |_| {})
633 }
634
635 pub fn handle_events_immediately_with(&mut self, observer: &mut dyn FnMut(TasksPollStage)) {
641 while let Ok(msg) = self.receiver.try_recv() {
642 match msg {
643 Message::MarkScopeAsDirty(scope_id) => {
644 self.dirty_scopes.insert(scope_id);
645 }
646 Message::PollTask(task_id) => {
647 self.dirty_tasks.push_back(task_id);
648 }
649 }
650 }
651
652 self.poll_dirty_tasks(observer);
653 }
654
655 fn poll_dirty_tasks(&mut self, observer: &mut dyn FnMut(TasksPollStage)) {
657 if self.dirty_tasks.is_empty() {
658 return;
659 }
660
661 observer(TasksPollStage::Started);
662
663 while let Some(task_id) = self.dirty_tasks.pop_front() {
664 let Some(task) = self.tasks.borrow().get(&task_id).cloned() else {
665 continue;
666 };
667 let mut task = task.borrow_mut();
668 let waker = task.waker.clone();
669
670 let mut cx = std::task::Context::from_waker(&waker);
671
672 CurrentContext::run(
673 {
674 let Some(scope) = self.scopes.get(&task.scope_id) else {
675 continue;
676 };
677 self.create_context(scope.borrow().id)
678 },
679 || {
680 let poll_result = task.future.poll(&mut cx);
681 if poll_result.is_ready() {
682 let _ = self.tasks.borrow_mut().remove(&task_id);
683 }
684 },
685 );
686 }
687
688 observer(TasksPollStage::Finished);
689 }
690
691 #[cfg_attr(feature = "hotpath", hotpath::measure)]
692 pub fn sync_and_update(&mut self) -> Mutations {
693 self.handle_events_immediately();
694 use itertools::Itertools;
695
696 if self.dirty_scopes.is_empty() {
698 return Mutations::default();
699 }
700
701 #[cfg(all(debug_assertions, feature = "debug-integrity"))]
702 self.verify_scopes_integrity();
703
704 let mut mutations = Mutations::default();
705 let mut pending_reorders = FxHashMap::default();
706
707 let dirty_scopes = self
708 .dirty_scopes
709 .drain()
710 .filter_map(|id| self.scopes.get(&id).cloned())
711 .sorted_by_key(|s| s.borrow().height)
712 .map(|s| s.borrow().id)
713 .collect::<Box<[_]>>();
714
715 let mut visited_scopes = FxHashSet::default();
716
717 for scope_id in dirty_scopes {
718 if visited_scopes.contains(&scope_id) {
720 continue;
721 }
722
723 let Some(scope_rc) = self.scopes.get(&scope_id).cloned() else {
724 continue;
725 };
726
727 let scope_id = scope_rc.borrow().id;
728
729 let element = CurrentContext::run_with_reactive(self.create_context(scope_id), || {
730 let scope = scope_rc.borrow();
731 #[cfg(feature = "hotreload")]
732 {
733 subsecond::call(|| (scope.comp)(scope.props.clone()))
734 }
735 #[cfg(not(feature = "hotreload"))]
736 {
737 (scope.comp)(scope.props.clone())
738 }
739 });
740
741 let path_element = PathElement::from_element(vec![0], element);
742 let mut diff = Diff::default();
743 path_element.diff(scope_rc.borrow().element.as_ref(), &mut diff);
744
745 self.apply_diff(
746 &scope_rc,
747 diff,
748 &mut mutations,
749 &path_element,
750 &mut pending_reorders,
751 );
752
753 self.run_scope(
754 &scope_rc,
755 &path_element,
756 &mut mutations,
757 &mut visited_scopes,
758 &mut pending_reorders,
759 );
760
761 let mut scopes_storages = self.scopes_storages.borrow_mut();
762 let scope_storage = scopes_storages.get_mut(&scope_rc.borrow().id).unwrap();
763 scope_storage.current_value = 0;
764 scope_storage.current_run += 1;
765
766 scope_rc.borrow_mut().element = Some(path_element);
767 }
768
769 for (parent_node_id, path) in pending_reorders {
771 let Some(scope_id) = self.node_to_scope.get(&parent_node_id) else {
772 continue;
773 };
774 let scope = self.scopes[scope_id].borrow();
775 let mut children = Vec::with_capacity(scope.nodes.len(&path).unwrap_or_default());
776 scope.nodes.traverse_1_level(&path, |_, node| {
777 children.push(match node.scope_id {
778 Some(scope_id) => self.find_scope_root_node_id(scope_id),
779 None => node.node_id,
780 });
781 });
782 mutations.moved.insert(parent_node_id, children);
783 }
784
785 mutations
786 }
787
788 pub fn run_in<T>(&self, run: impl FnOnce() -> T) -> T {
789 CurrentContext::run(self.create_context(ScopeId::ROOT), run)
790 }
791
792 #[cfg_attr(feature = "hotpath", hotpath::measure)]
793 fn run_scope(
794 &mut self,
795 scope: &Rc<RefCell<Scope>>,
796 element: &PathElement,
797 mutations: &mut Mutations,
798 visited_scopes: &mut FxHashSet<ScopeId>,
799 pending_reorders: &mut ReorderedParents,
800 ) {
801 visited_scopes.insert(scope.borrow().id);
802 match element {
803 PathElement::Component {
804 comp,
805 props,
806 key,
807 path,
808 } => {
809 let assigned_scope_id = scope
811 .borrow()
812 .nodes
813 .get(path)
814 .and_then(|path_node| path_node.scope_id)
815 .unwrap();
816
817 let parent_node_id = if path.as_ref() == [0] {
818 scope.borrow().parent_node_id_in_parent
819 } else {
820 scope
821 .borrow()
822 .nodes
823 .get(&path[..path.len() - 1])
824 .unwrap()
825 .node_id
826 };
827
828 if let Some(Ok(mut existing_scope)) = self
829 .scopes
830 .get(&assigned_scope_id)
831 .map(|s| s.try_borrow_mut())
832 {
833 let key_changed = existing_scope.key != *key;
834 let type_changed = (existing_scope.props.as_ref() as &dyn Any).type_id()
836 != (props.as_ref() as &dyn Any).type_id();
837 if key_changed || type_changed || existing_scope.props.changed(props.as_ref()) {
838 self.dirty_scopes.insert(assigned_scope_id);
839 existing_scope.props = props.clone();
840
841 if key_changed || type_changed {
842 existing_scope.key = key.clone();
843 existing_scope.comp = comp.clone();
844 self.scopes_storages
845 .borrow_mut()
846 .get_mut(&assigned_scope_id)
847 .unwrap()
848 .reset();
849 }
850 }
851 } else {
852 self.scopes.insert(
853 assigned_scope_id,
854 Rc::new(RefCell::new(Scope {
855 parent_node_id_in_parent: parent_node_id,
856 path_in_parent: path.clone(),
857 height: scope.borrow().height + 1,
858 parent_id: Some(scope.borrow().id),
859 id: assigned_scope_id,
860 key: key.clone(),
861 comp: comp.clone(),
862 props: props.clone(),
863 element: None,
864 nodes: PathGraph::default(),
865 })),
866 );
867 self.scopes_storages.borrow_mut().insert(
868 assigned_scope_id,
869 ScopeStorage::new(Some(scope.borrow().id), |writer| {
870 ReactiveContext::new_for_scope(
871 self.sender.clone(),
872 assigned_scope_id,
873 writer,
874 )
875 }),
876 );
877 self.dirty_scopes.insert(assigned_scope_id);
878 }
879
880 let was_dirty = self.dirty_scopes.remove(&assigned_scope_id);
881
882 if !was_dirty {
883 return;
885 }
886
887 let scope_rc = self.scopes.get(&assigned_scope_id).cloned().unwrap();
888
889 let element = hotpath::measure_block!("Scope Rendering", {
890 CurrentContext::run_with_reactive(
891 self.create_context(assigned_scope_id),
892 || {
893 let scope = scope_rc.borrow();
894 #[cfg(feature = "hotreload")]
895 {
896 subsecond::call(|| (scope.comp)(scope.props.clone()))
897 }
898 #[cfg(not(feature = "hotreload"))]
899 {
900 (scope.comp)(scope.props.clone())
901 }
902 },
903 )
904 });
905
906 let path_element = PathElement::from_element(vec![0], element);
907 let mut diff = Diff::default();
908 path_element.diff(scope_rc.borrow().element.as_ref(), &mut diff);
909
910 self.apply_diff(&scope_rc, diff, mutations, &path_element, pending_reorders);
911
912 self.run_scope(
913 &scope_rc,
914 &path_element,
915 mutations,
916 visited_scopes,
917 pending_reorders,
918 );
919 let mut scopes_storages = self.scopes_storages.borrow_mut();
920 let scope_storage = scopes_storages.get_mut(&assigned_scope_id).unwrap();
921 scope_storage.current_value = 0;
922 scope_storage.current_run += 1;
923
924 scope_rc.borrow_mut().element = Some(path_element);
925 }
926 PathElement::Element { elements, .. } => {
927 for element in elements.iter() {
928 self.run_scope(scope, element, mutations, visited_scopes, pending_reorders);
929 }
930 }
931 }
932 }
933
934 fn find_scope_root_parent_info(
937 &self,
938 parent_id: Option<ScopeId>,
939 parent_node_id: NodeId,
940 scope_id: ScopeId,
941 ) -> (NodeId, u32) {
942 let mut index_inside_parent = 0;
943
944 if let Some(parent_id) = parent_id {
945 let mut buffer = Some((parent_id, parent_node_id, scope_id));
946 while let Some((parent_id, parent_node_id, scope_id)) = buffer.take() {
947 let parent_scope = self.scopes.get(&parent_id).unwrap();
948 let parent_scope = parent_scope.borrow();
949
950 let scope = self.scopes.get(&scope_id).unwrap();
951 let scope = scope.borrow();
952
953 let path_node_parent = parent_scope.nodes.find(|v| {
954 if let Some(v) = v {
955 v.node_id == parent_node_id
956 } else {
957 false
958 }
959 });
960
961 if let Some(path_node_parent) = path_node_parent {
962 if let Some(scope_id) = path_node_parent.scope_id {
963 if let Some(parent_id) = parent_scope.parent_id {
964 buffer.replace((
966 parent_id,
967 parent_scope.parent_node_id_in_parent,
968 scope_id,
969 ));
970 } else {
971 assert_eq!(scope_id, ScopeId::ROOT);
972 }
973 } else {
974 index_inside_parent = *scope.path_in_parent.last().unwrap();
976 return (parent_node_id, index_inside_parent);
977 }
978 } else if let Some(new_parent_id) = parent_scope.parent_id {
979 buffer.replace((
981 new_parent_id,
982 parent_scope.parent_node_id_in_parent,
983 parent_id,
984 ));
985 }
986 }
987 } else {
988 assert_eq!(scope_id, ScopeId::ROOT);
989 }
990
991 (parent_node_id, index_inside_parent)
992 }
993
994 fn find_scope_root_node_id(&self, scope_id: ScopeId) -> NodeId {
998 let scope_rc = self.scopes.get(&scope_id).unwrap();
999 let scope = scope_rc.borrow();
1000 let path_node = scope.nodes.get(&[0]).unwrap();
1001 if let Some(child_scope_id) = path_node.scope_id {
1002 self.find_scope_root_node_id(child_scope_id)
1003 } else {
1004 path_node.node_id
1005 }
1006 }
1007
1008 fn process_addition(
1011 &mut self,
1012 scope: &Rc<RefCell<Scope>>,
1013 added: &[u32],
1014 path_element: &PathElement,
1015 mutations: &mut Mutations,
1016 parents_to_resync_scopes: &mut FxHashSet<Box<[u32]>>,
1017 addition_parents: &mut AdditionParents,
1018 ) {
1019 let (parent_node_id, index_inside_parent, insertion_path) = if added == [0] {
1020 let parent_id = scope.borrow().parent_id;
1021 let scope_id = scope.borrow().id;
1022 let parent_node_id = scope.borrow().parent_node_id_in_parent;
1023 let (parent_node_id, index_inside_parent) =
1024 self.find_scope_root_parent_info(parent_id, parent_node_id, scope_id);
1025 (parent_node_id, index_inside_parent, vec![0])
1026 } else {
1027 let parent_path = &added[..added.len() - 1];
1028 parents_to_resync_scopes.insert(Box::from(parent_path));
1029
1030 let (parent_node_id, cached_path) = addition_parents.get_mut(parent_path).unwrap();
1031 let parent_node_id = *parent_node_id;
1032
1033 let mut insertion_path = cached_path
1035 .get_or_insert_with(|| {
1036 let is_at_new_path = scope
1038 .borrow()
1039 .nodes
1040 .get(parent_path)
1041 .is_some_and(|node| node.node_id == parent_node_id);
1042 if is_at_new_path {
1043 parent_path.to_vec()
1044 } else {
1045 scope
1046 .borrow()
1047 .nodes
1048 .find_path(|v| v.is_some_and(|node| node.node_id == parent_node_id))
1049 .unwrap()
1050 }
1051 })
1052 .clone();
1053 insertion_path.push(added[added.len() - 1]);
1054 (parent_node_id, added[added.len() - 1], insertion_path)
1055 };
1056
1057 self.node_id_counter += 1;
1058
1059 path_element.with_element(added, |element| {
1060 let scope_id = match element {
1061 PathElement::Component { .. } => {
1062 self.scope_id_counter += 1;
1063 Some(self.scope_id_counter)
1064 }
1065 PathElement::Element { element, .. } => {
1066 mutations.added.push(MutationAdd {
1067 node_id: self.node_id_counter,
1068 parent_id: parent_node_id,
1069 index: index_inside_parent,
1070 element: element.clone(),
1071 });
1072 self.node_to_scope
1073 .insert(self.node_id_counter, scope.borrow().id);
1074 None
1075 }
1076 };
1077
1078 scope.borrow_mut().nodes.insert(
1079 &insertion_path,
1080 PathNode {
1081 node_id: self.node_id_counter,
1082 scope_id,
1083 },
1084 );
1085 addition_parents.insert(
1086 Box::from(added),
1087 (self.node_id_counter, Some(insertion_path)),
1088 );
1089 });
1090 }
1091
1092 #[cfg_attr(feature = "hotpath", hotpath::measure)]
1093 fn apply_diff(
1094 &mut self,
1095 scope: &Rc<RefCell<Scope>>,
1096 diff: Diff,
1097 mutations: &mut Mutations,
1098 path_element: &PathElement,
1099 pending_reorders: &mut ReorderedParents,
1100 ) {
1101 let mut moved_nodes = FxHashMap::<Box<[u32]>, Vec<Option<NodeId>>>::default();
1102 let mut parents_to_resync_scopes = FxHashSet::default();
1103
1104 for parent in diff.moved.keys() {
1106 parents_to_resync_scopes.insert(parent.clone());
1107 let old_parent = resolve_old_path(parent, &diff.moved);
1110 let parent_node_id = scope.borrow().nodes.get(&old_parent).unwrap().node_id;
1111 let mut children =
1112 vec![None; scope.borrow().nodes.len(&old_parent).unwrap_or_default()];
1113 scope
1114 .borrow()
1115 .nodes
1116 .traverse_1_level(&old_parent, |path, node| {
1117 children[path[path.len() - 1] as usize] = Some(node.node_id);
1118 });
1119 pending_reorders.insert(parent_node_id, parent.clone());
1120 moved_nodes.insert(parent.clone(), children);
1121 }
1122
1123 let mut addition_parents = FxHashMap::default();
1125 for added in &diff.added {
1126 if added.len() <= 1 {
1127 continue;
1128 }
1129 let parent = &added[..added.len() - 1];
1130 if addition_parents.contains_key(parent) {
1131 continue;
1132 }
1133 let old_parent = resolve_old_path(parent, &diff.moved);
1134 if let Some(node) = scope.borrow().nodes.get(&old_parent) {
1135 addition_parents.insert(Box::from(parent), (node.node_id, None));
1136 }
1137 }
1138
1139 let mut selected_roots: FxHashMap<&[u32], FxHashSet<&[u32]>> = FxHashMap::default();
1141 let mut scope_removal_buffer = vec![];
1142
1143 'remove: for removed in diff.removed.iter().sorted_by(|a, b| {
1163 for (x, y) in a.iter().zip(b.iter()) {
1164 match x.cmp(y) {
1165 Ordering::Equal => continue,
1166 non_eq => return non_eq.reverse(),
1167 }
1168 }
1169 b.len().cmp(&a.len())
1170 }) {
1171 parents_to_resync_scopes.insert(Box::from(&removed[..removed.len() - 1]));
1172
1173 let path_node = scope.borrow().nodes.get(removed).cloned();
1174 if let Some(PathNode { node_id, scope_id }) = path_node {
1175 if scope_id.is_none() {
1176 let removal = if removed.as_ref() == [0] {
1177 MutationRemove::Scope { id: node_id }
1178 } else {
1179 MutationRemove::Element {
1180 id: node_id,
1181 index: removed[removed.len() - 1],
1182 }
1183 };
1184 mutations.removed.push(removal);
1185 }
1186
1187 for (root, inner) in &mut selected_roots {
1189 if is_descendant(removed, root) {
1190 inner.insert(removed);
1191 continue 'remove;
1192 }
1193 }
1194
1195 selected_roots.retain(|root, _| !is_descendant(root, removed));
1197
1198 selected_roots
1199 .entry(&removed[..removed.len() - 1])
1200 .or_default()
1201 .insert(removed);
1202 } else {
1203 unreachable!()
1204 }
1205 }
1206
1207 for (root, removed) in selected_roots.iter().sorted_by(|(a, _), (b, _)| {
1209 for (x, y) in a.iter().zip(b.iter()) {
1210 match x.cmp(y) {
1211 Ordering::Equal => continue,
1212 non_eq => return non_eq.reverse(),
1213 }
1214 }
1215 b.len().cmp(&a.len())
1216 }) {
1217 scope.borrow_mut().nodes.retain(
1218 root,
1219 |p, _| !removed.contains(p),
1220 |_, &PathNode { scope_id, node_id }| {
1221 if let Some(scope_id) = scope_id {
1222 scope_removal_buffer.push(self.scopes.get(&scope_id).cloned().unwrap());
1224 } else {
1225 self.node_to_scope.remove(&node_id).unwrap();
1226 }
1227 },
1228 );
1229 }
1230
1231 let mut scope_removal_queue = VecDeque::new();
1232
1233 while let Some(scope_rc) = scope_removal_buffer.pop() {
1234 scope_removal_queue.push_front(scope_rc.clone());
1237
1238 let scope = scope_rc.borrow_mut();
1239
1240 let mut scope_root_node_id = None;
1241
1242 scope
1244 .nodes
1245 .traverse(&[], |path, &PathNode { scope_id, node_id }| {
1246 if let Some(scope_id) = scope_id {
1247 scope_removal_buffer.push(self.scopes.get(&scope_id).cloned().unwrap());
1248 } else {
1249 self.node_to_scope.remove(&node_id).unwrap();
1250 }
1251 if path == [0] {
1252 scope_root_node_id = Some(node_id);
1253 }
1254 });
1255
1256 mutations.removed.push(MutationRemove::Scope {
1259 id: scope_root_node_id.unwrap(),
1260 });
1261 }
1262
1263 for scope_rc in scope_removal_queue {
1265 let scope = scope_rc.borrow_mut();
1266
1267 self.scopes.remove(&scope.id);
1268
1269 CurrentContext::run_with_reactive(self.create_context(scope.id), || {
1271 let mut _removed_tasks = Vec::new();
1273
1274 self.tasks.borrow_mut().retain(|task_id, task| {
1275 if task.borrow().scope_id == scope.id {
1276 _removed_tasks.push((*task_id, task.clone()));
1277 false
1278 } else {
1279 true
1280 }
1281 });
1282 drop(_removed_tasks);
1283 let _scope = self.scopes_storages.borrow_mut().remove(&scope.id);
1285 });
1286 }
1287
1288 for added in diff
1308 .added
1309 .iter()
1310 .sorted_by(|a, b| {
1311 for (x, y) in a.iter().zip(b.iter()) {
1312 match x.cmp(y) {
1313 Ordering::Equal => continue,
1314 non_eq => return non_eq.reverse(),
1315 }
1316 }
1317 b.len().cmp(&a.len())
1318 })
1319 .rev()
1320 {
1321 self.process_addition(
1322 scope,
1323 added,
1324 path_element,
1325 mutations,
1326 &mut parents_to_resync_scopes,
1327 &mut addition_parents,
1328 );
1329 }
1330
1331 let added = if diff.moved.is_empty() {
1332 FxHashSet::default()
1333 } else {
1334 diff.added.iter().map(|path| path.as_ref()).collect()
1335 };
1336
1337 for (parent, movements) in diff.moved.into_iter().sorted_by(|(a, _), (b, _)| {
1338 for (x, y) in a.iter().zip(b.iter()) {
1339 match x.cmp(y) {
1340 Ordering::Equal => continue,
1341 non_eq => return non_eq.reverse(),
1342 }
1343 }
1344 a.len().cmp(&b.len())
1345 }) {
1346 let old_children = moved_nodes.remove(&parent).unwrap();
1347 let sources = movements
1349 .into_iter()
1350 .map(|(from, to)| (to, from))
1351 .collect::<FxHashMap<_, _>>();
1352
1353 let mut child_count = 0;
1355 path_element.with_element(&parent, |element| {
1356 if let PathElement::Element { elements, .. } = element {
1357 child_count = elements.len() as u32;
1358 }
1359 });
1360
1361 let mut to_path = Vec::with_capacity(parent.len() + 1);
1362 to_path.extend_from_slice(&parent);
1363 to_path.push(0);
1364
1365 for to in 0..child_count {
1367 to_path[parent.len()] = to;
1368
1369 let is_added = added.contains(to_path.as_slice());
1370 let node_id = if is_added {
1371 addition_parents.get(to_path.as_slice()).unwrap().0
1372 } else {
1373 let from = sources.get(&to).copied().unwrap_or(to);
1375 old_children[from as usize].expect("surviving child must have an old node")
1376 };
1377
1378 if scope
1379 .borrow()
1380 .nodes
1381 .get(&to_path)
1382 .is_some_and(|node| node.node_id == node_id)
1383 {
1384 continue;
1385 }
1386
1387 let current_path = scope
1388 .borrow()
1389 .nodes
1390 .find_child_path(&parent, |v| v.is_some_and(|v| v.node_id == node_id))
1391 .unwrap();
1392
1393 let path_entry = scope.borrow_mut().nodes.remove(¤t_path).unwrap();
1395 scope.borrow_mut().nodes.insert_entry(&to_path, path_entry);
1396 }
1397 }
1398
1399 for (modified, flags) in diff.modified {
1400 path_element.with_element(&modified, |element| match element {
1401 PathElement::Component { .. } => {
1402 unreachable!()
1404 }
1405 PathElement::Element { element, .. } => {
1406 let node_id = scope
1407 .borrow()
1408 .nodes
1409 .get(&modified)
1410 .map(|path_node| path_node.node_id)
1411 .unwrap();
1412 mutations.modified.push(MutationModified {
1413 node_id,
1414 element: element.clone(),
1415 flags,
1416 });
1417 }
1418 });
1419 }
1420
1421 for parent in parents_to_resync_scopes {
1424 if diff.added.contains(&parent) {
1427 continue;
1429 }
1430
1431 scope
1433 .borrow_mut()
1434 .nodes
1435 .traverse_1_level(&parent, |p, path_node| {
1436 if let Some(scope_id) = path_node.scope_id
1437 && let Some(scope_rc) = self.scopes.get(&scope_id)
1438 {
1439 let mut scope = scope_rc.borrow_mut();
1440 scope.path_in_parent = Box::from(p);
1441 }
1442 });
1443 }
1444 }
1445
1446 pub fn reload(&mut self) {
1451 self.tasks.borrow_mut().clear();
1452 self.dirty_tasks.clear();
1453 while self.receiver.try_recv().is_ok() {}
1454
1455 let scopes = self
1456 .scopes
1457 .iter()
1458 .sorted_by_key(|(_, s)| s.borrow().height)
1459 .map(|(_, s)| s.borrow().id)
1460 .collect::<Vec<_>>();
1461
1462 for scope_id in scopes {
1463 CurrentContext::run(self.create_context(scope_id), || {
1464 let _hooks = self
1465 .scopes_storages
1466 .borrow_mut()
1467 .get_mut(&scope_id)
1468 .map(|storage| storage.reset_hooks());
1469 });
1470 }
1471
1472 self.dirty_scopes.extend(self.scopes.keys());
1473 let _ = self
1474 .sender
1475 .unbounded_send(Message::MarkScopeAsDirty(ScopeId::ROOT));
1476 }
1477}
1478
1479pub type Diff = tiny_diff::Diff<DiffModifies>;
1480
1481fn resolve_old_path(new_path: &[u32], moved: &FxHashMap<Box<[u32]>, Vec<(u32, u32)>>) -> Vec<u32> {
1486 let mut old_path = Vec::with_capacity(new_path.len());
1487 for i in 0..new_path.len() {
1488 let new_parent = &new_path[..i];
1489 let new_index = new_path[i];
1490 if let Some(movements) = moved.get(new_parent)
1491 && let Some(&(from, _)) = movements.iter().find(|(_, to)| *to == new_index)
1492 {
1493 old_path.push(from);
1494 continue;
1495 }
1496 old_path.push(new_index);
1497 }
1498 old_path
1499}
1500
1501fn is_descendant(candidate: &[u32], ancestor: &[u32]) -> bool {
1502 if ancestor.len() > candidate.len() {
1503 return false;
1504 }
1505 candidate[..ancestor.len()] == *ancestor
1506}