1use std::{
2 any::{
3 Any,
4 TypeId,
5 },
6 cell::RefCell,
7 cmp::Ordering,
8 collections::{
9 HashMap,
10 HashSet,
11 VecDeque,
12 },
13 fmt::Debug,
14 rc::Rc,
15 sync::atomic::AtomicU64,
16};
17
18use futures_lite::{
19 FutureExt,
20 StreamExt,
21};
22use itertools::Itertools;
23use pathgraph::PathGraph;
24use rustc_hash::{
25 FxHashMap,
26 FxHashSet,
27};
28
29use crate::{
30 current_context::CurrentContext,
31 diff_key::DiffKey,
32 element::{
33 Element,
34 ElementExt,
35 EventHandlerType,
36 },
37 events::{
38 data::{
39 Event,
40 EventType,
41 },
42 name::EventName,
43 },
44 node_id::NodeId,
45 path_element::PathElement,
46 prelude::{
47 Task,
48 TaskId,
49 },
50 reactive_context::ReactiveContext,
51 scope::{
52 PathNode,
53 Scope,
54 ScopeStorage,
55 },
56 scope_id::ScopeId,
57 tree::DiffModifies,
58};
59
60#[derive(Debug, PartialEq, Eq)]
61pub enum MutationRemove {
62 Element { id: NodeId, index: u32 },
64 Scope { id: NodeId },
66}
67
68impl PartialOrd for MutationRemove {
69 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
70 Some(self.cmp(other))
71 }
72}
73
74impl Ord for MutationRemove {
75 fn cmp(&self, other: &Self) -> Ordering {
76 use MutationRemove::*;
77 match (self, other) {
78 (Element { index: a, .. }, Element { index: b, .. }) => b.cmp(a),
80 (Element { .. }, Scope { .. }) => Ordering::Less,
82 (Scope { .. }, Element { .. }) => Ordering::Greater,
83 (Scope { id: a }, Scope { id: b }) => b.cmp(a),
85 }
86 }
87}
88
89impl MutationRemove {
90 pub fn node_id(&self) -> NodeId {
91 match self {
92 Self::Element { id, .. } => *id,
93 Self::Scope { id } => *id,
94 }
95 }
96}
97
98pub struct MutationAdd {
99 pub node_id: NodeId,
100 pub parent_id: NodeId,
101 pub index: u32,
102 pub element: Rc<dyn ElementExt>,
103}
104
105pub struct MutationModified {
106 pub node_id: NodeId,
107 pub element: Rc<dyn ElementExt>,
108 pub flags: DiffModifies,
109}
110
111#[derive(Debug)]
112pub struct MutationMove {
113 pub index: u32,
114 pub node_id: NodeId,
115}
116
117#[derive(Default)]
118pub struct Mutations {
119 pub added: Vec<MutationAdd>,
120 pub modified: Vec<MutationModified>,
121 pub removed: Vec<MutationRemove>,
122 pub moved: HashMap<NodeId, Vec<MutationMove>>,
123}
124
125impl Debug for Mutations {
126 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
127 f.write_fmt(format_args!(
128 "Added: {:?} Modified: {:?} Removed: {:?} Moved: {:?}",
129 self.added
130 .iter()
131 .map(|m| (m.node_id, m.parent_id, m.index))
132 .collect::<Vec<_>>(),
133 self.modified.iter().map(|m| m.node_id).collect::<Vec<_>>(),
134 self.removed,
135 self.moved
136 .iter()
137 .map(|(parent_id, moves)| {
138 (
139 parent_id,
140 moves
141 .iter()
142 .map(|m| (m.index, m.node_id))
143 .collect::<Vec<_>>(),
144 )
145 })
146 .collect::<Vec<_>>()
147 ))
148 }
149}
150
151pub enum Message {
152 MarkScopeAsDirty(ScopeId),
153 PollTask(TaskId),
154}
155
156#[derive(Clone, Copy, PartialEq, Eq, Debug)]
158pub enum TasksPollStage {
159 Started,
160 Finished,
161}
162
163pub struct Runner {
164 pub scopes: FxHashMap<ScopeId, Rc<RefCell<Scope>>>,
165 pub scopes_storages: Rc<RefCell<FxHashMap<ScopeId, ScopeStorage>>>,
166
167 pub(crate) dirty_scopes: FxHashSet<ScopeId>,
168 pub(crate) dirty_tasks: VecDeque<TaskId>,
169
170 pub node_to_scope: FxHashMap<NodeId, ScopeId>,
171
172 pub(crate) node_id_counter: NodeId,
173 pub(crate) scope_id_counter: ScopeId,
174 pub(crate) task_id_counter: Rc<AtomicU64>,
175
176 pub(crate) tasks: Rc<RefCell<FxHashMap<TaskId, Rc<RefCell<Task>>>>>,
177
178 pub(crate) sender: futures_channel::mpsc::UnboundedSender<Message>,
179 pub(crate) receiver: futures_channel::mpsc::UnboundedReceiver<Message>,
180}
181
182impl Debug for Runner {
183 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
184 f.debug_struct("Runner")
185 .field("dirty_scopes", &self.dirty_scopes.len())
186 .field("dirty_tasks", &self.dirty_tasks.len())
187 .field("node_to_scope", &self.node_to_scope.len())
188 .field("scopes", &self.scopes.len())
189 .field("scopes_storages", &self.scopes_storages.borrow().len())
190 .field("tasks", &self.tasks.borrow().len())
191 .finish()
192 }
193}
194
195impl Drop for Runner {
196 fn drop(&mut self) {
197 for (scope_id, _scope) in self
199 .scopes
200 .drain()
201 .sorted_by_key(|s| s.1.borrow().height)
202 .rev()
203 {
204 CurrentContext::run_with_reactive(
205 CurrentContext {
206 scope_id,
207 scopes_storages: self.scopes_storages.clone(),
208 tasks: self.tasks.clone(),
209 task_id_counter: self.task_id_counter.clone(),
210 sender: self.sender.clone(),
211 },
212 || {
213 let mut _removed_tasks = Vec::new();
214
215 self.tasks.borrow_mut().retain(|task_id, task| {
216 if task.borrow().scope_id == scope_id {
217 _removed_tasks.push((*task_id, task.clone()));
218 false
219 } else {
220 true
221 }
222 });
223 drop(_removed_tasks);
224 let _scope = self.scopes_storages.borrow_mut().remove(&scope_id);
225 },
226 );
227 }
228 }
229}
230
231impl Runner {
232 pub fn new(root: impl Fn() -> Element + 'static) -> Self {
233 let (sender, receiver) = futures_channel::mpsc::unbounded::<Message>();
234 Self {
235 scopes: FxHashMap::from_iter([(
236 ScopeId::ROOT,
237 Rc::new(RefCell::new(Scope {
238 parent_node_id_in_parent: NodeId::ROOT,
239 path_in_parent: Box::from([]),
240 height: 0,
241 parent_id: None,
242 id: ScopeId::ROOT,
243 key: DiffKey::Root,
244 comp: Rc::new(move |_| root()),
245 props: Rc::new(()),
246 element: None,
247 nodes: {
248 let mut map = PathGraph::new();
249 map.insert(
250 &[],
251 PathNode {
252 node_id: NodeId::ROOT,
253 scope_id: None,
254 },
255 );
256 map
257 },
258 })),
259 )]),
260 scopes_storages: Rc::new(RefCell::new(FxHashMap::from_iter([(
261 ScopeId::ROOT,
262 ScopeStorage::new(None, |writer| {
263 ReactiveContext::new_for_scope(sender.clone(), ScopeId::ROOT, writer)
264 }),
265 )]))),
266
267 node_to_scope: FxHashMap::from_iter([(NodeId::ROOT, ScopeId::ROOT)]),
268
269 node_id_counter: NodeId::ROOT,
270 scope_id_counter: ScopeId::ROOT,
271 task_id_counter: Rc::default(),
272
273 dirty_tasks: VecDeque::default(),
274 dirty_scopes: FxHashSet::from_iter([ScopeId::ROOT]),
275
276 tasks: Rc::default(),
277
278 sender,
279 receiver,
280 }
281 }
282
283 #[cfg(all(debug_assertions, feature = "debug-integrity"))]
284 #[cfg_attr(feature = "hotpath", hotpath::measure)]
285 pub fn verify_scopes_integrity(&self) {
286 let mut visited = FxHashSet::default();
287 let size = self.scopes.len();
288 let mut buffer = vec![ScopeId::ROOT];
289 while let Some(scope_id) = buffer.pop() {
290 if visited.contains(&scope_id) {
291 continue;
292 }
293 visited.insert(scope_id);
294 let scope = self.scopes.get(&scope_id).unwrap();
295 let scope = scope.borrow();
296 if let Some(parent) = scope.parent_id {
297 buffer.push(parent);
298 }
299 scope.nodes.traverse(&[], |_, &PathNode { scope_id, .. }| {
300 if let Some(scope_id) = scope_id {
301 buffer.push(scope_id);
302 }
303 });
304 }
305 assert_eq!(size, visited.len())
306 }
307
308 pub fn provide_root_context<T: 'static + Clone>(&mut self, context: impl FnOnce() -> T) -> T {
309 CurrentContext::run(
310 CurrentContext {
311 scope_id: ScopeId::ROOT,
312 scopes_storages: self.scopes_storages.clone(),
313 tasks: self.tasks.clone(),
314 task_id_counter: self.task_id_counter.clone(),
315 sender: self.sender.clone(),
316 },
317 move || {
318 let context = context();
319 let mut scopes_storages = self.scopes_storages.borrow_mut();
320 let root_scope_storage = scopes_storages.get_mut(&ScopeId::ROOT).unwrap();
321 root_scope_storage
322 .contexts
323 .insert(TypeId::of::<T>(), Rc::new(context.clone()));
324
325 context
326 },
327 )
328 }
329
330 pub fn handle_event(
331 &mut self,
332 node_id: impl Into<NodeId>,
333 event_name: EventName,
334 event_type: EventType,
335 bubbles: bool,
336 ) -> bool {
337 let node_id = node_id.into();
338 #[cfg(debug_assertions)]
339 tracing::info!("Handling event {event_name:?} for {node_id:?}");
340 let propagate = Rc::new(RefCell::new(bubbles));
341 let default = Rc::new(RefCell::new(true));
342
343 let Some(scope_id) = self.node_to_scope.get(&node_id) else {
344 return false;
345 };
346 let Some(path) = self
347 .scopes
348 .get(scope_id)
349 .unwrap()
350 .borrow()
351 .nodes
352 .find_path(|value| {
353 value
354 == Some(&PathNode {
355 node_id,
356 scope_id: None,
357 })
358 })
359 else {
360 return false;
361 };
362
363 let mut current_target = Some((path, *scope_id));
364 while let Some((path, scope_id)) = current_target.take() {
365 let scope = self.scopes.get(&scope_id).cloned().unwrap();
366 scope.borrow().with_element(&path, |element| {
367 match element {
368 PathElement::Component { .. } => {
369 unreachable!()
370 }
371 PathElement::Element { element, .. } => {
372 CurrentContext::run(
373 CurrentContext {
374 scope_id,
375 scopes_storages: self.scopes_storages.clone(),
376 tasks: self.tasks.clone(),
377 task_id_counter: self.task_id_counter.clone(),
378 sender: self.sender.clone(),
379 },
380 || {
381 match &event_type {
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::Styled(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::Styled(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::Wheel(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::Wheel(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::Touch(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::Touch(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::Pointer(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::Pointer(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::File(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::File(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::ImePreedit(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::ImePreedit(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 }
527
528 if *propagate.borrow() {
530 if path.len() > 1 {
531 current_target
533 .replace((path[..path.len() - 1].to_vec(), scope_id));
534 } else {
535 let mut parent_scope_id = scope.borrow().parent_id;
536 loop {
538 if let Some(parent_id) = parent_scope_id.take() {
539 let parent_scope =
540 self.scopes.get(&parent_id).unwrap();
541 let path = parent_scope.borrow().nodes.find_path(
542 |value| {
543 value
544 == Some(&PathNode {
545 node_id: scope
546 .borrow()
547 .parent_node_id_in_parent,
548 scope_id: None,
549 })
550 },
551 );
552 if let Some(path) = path {
553 current_target.replace((path, parent_id));
554 break;
555 } else {
556 parent_scope_id =
557 parent_scope.borrow().parent_id;
558 }
559 } else {
560 return;
561 }
562 }
563 }
564 }
565 },
566 )
567 }
568 }
569 });
570 }
571 *default.borrow()
572 }
573
574 #[cfg_attr(feature = "hotpath", hotpath::measure)]
575 pub async fn handle_events(&mut self) {
576 self.handle_events_with(&mut |_| {}).await
577 }
578
579 pub async fn handle_events_with(&mut self, observer: &mut dyn FnMut(TasksPollStage)) {
581 loop {
582 while let Ok(msg) = self.receiver.try_recv() {
583 match msg {
584 Message::MarkScopeAsDirty(scope_id) => {
585 self.dirty_scopes.insert(scope_id);
586 }
587 Message::PollTask(task_id) => {
588 self.dirty_tasks.push_back(task_id);
589 }
590 }
591 }
592
593 if !self.dirty_scopes.is_empty() {
594 return;
595 }
596
597 self.poll_dirty_tasks(observer);
598
599 if !self.dirty_scopes.is_empty() {
600 return;
601 }
602
603 let Some(msg) = self.receiver.next().await else {
604 return;
605 };
606 match msg {
607 Message::MarkScopeAsDirty(scope_id) => {
608 self.dirty_scopes.insert(scope_id);
609 }
610 Message::PollTask(task_id) => {
611 self.dirty_tasks.push_back(task_id);
612 }
613 }
614 }
615 }
616
617 #[cfg_attr(feature = "hotpath", hotpath::measure)]
619 pub fn handle_events_immediately(&mut self) {
620 self.handle_events_immediately_with(&mut |_| {})
621 }
622
623 pub fn handle_events_immediately_with(&mut self, observer: &mut dyn FnMut(TasksPollStage)) {
626 while let Ok(msg) = self.receiver.try_recv() {
627 match msg {
628 Message::MarkScopeAsDirty(scope_id) => {
629 self.dirty_scopes.insert(scope_id);
630 }
631 Message::PollTask(task_id) => {
632 self.dirty_tasks.push_back(task_id);
633 }
634 }
635 }
636
637 if !self.dirty_scopes.is_empty() {
638 return;
639 }
640
641 self.poll_dirty_tasks(observer);
642 }
643
644 fn poll_dirty_tasks(&mut self, observer: &mut dyn FnMut(TasksPollStage)) {
646 if self.dirty_tasks.is_empty() {
647 return;
648 }
649
650 observer(TasksPollStage::Started);
651
652 while let Some(task_id) = self.dirty_tasks.pop_front() {
653 let Some(task) = self.tasks.borrow().get(&task_id).cloned() else {
654 continue;
655 };
656 let mut task = task.borrow_mut();
657 let waker = task.waker.clone();
658
659 let mut cx = std::task::Context::from_waker(&waker);
660
661 CurrentContext::run(
662 {
663 let Some(scope) = self.scopes.get(&task.scope_id) else {
664 continue;
665 };
666 CurrentContext {
667 scope_id: scope.borrow().id,
668 scopes_storages: self.scopes_storages.clone(),
669 tasks: self.tasks.clone(),
670 task_id_counter: self.task_id_counter.clone(),
671 sender: self.sender.clone(),
672 }
673 },
674 || {
675 let poll_result = task.future.poll(&mut cx);
676 if poll_result.is_ready() {
677 let _ = self.tasks.borrow_mut().remove(&task_id);
678 }
679 },
680 );
681 }
682
683 observer(TasksPollStage::Finished);
684 }
685
686 #[cfg_attr(feature = "hotpath", hotpath::measure)]
687 pub fn sync_and_update(&mut self) -> Mutations {
688 self.handle_events_immediately();
689 use itertools::Itertools;
690
691 #[cfg(all(debug_assertions, feature = "debug-integrity"))]
692 self.verify_scopes_integrity();
693
694 let mut mutations = Mutations::default();
695
696 let dirty_scopes = self
697 .dirty_scopes
698 .drain()
699 .filter_map(|id| self.scopes.get(&id).cloned())
700 .sorted_by_key(|s| s.borrow().height)
701 .map(|s| s.borrow().id)
702 .collect::<Box<[_]>>();
703
704 let mut visited_scopes = FxHashSet::default();
705
706 for scope_id in dirty_scopes {
707 if visited_scopes.contains(&scope_id) {
709 continue;
710 }
711
712 let Some(scope_rc) = self.scopes.get(&scope_id).cloned() else {
713 continue;
714 };
715
716 let scope_id = scope_rc.borrow().id;
717
718 let element = CurrentContext::run_with_reactive(
719 CurrentContext {
720 scope_id,
721 scopes_storages: self.scopes_storages.clone(),
722 tasks: self.tasks.clone(),
723 task_id_counter: self.task_id_counter.clone(),
724 sender: self.sender.clone(),
725 },
726 || {
727 let scope = scope_rc.borrow();
728 #[cfg(feature = "hotreload")]
729 {
730 subsecond::call(|| (scope.comp)(scope.props.clone()))
731 }
732 #[cfg(not(feature = "hotreload"))]
733 {
734 (scope.comp)(scope.props.clone())
735 }
736 },
737 );
738
739 let path_element = PathElement::from_element(vec![0], element);
740 let mut diff = Diff::default();
741 path_element.diff(scope_rc.borrow().element.as_ref(), &mut diff);
742
743 self.apply_diff(&scope_rc, diff, &mut mutations, &path_element);
744
745 self.run_scope(
746 &scope_rc,
747 &path_element,
748 &mut mutations,
749 &mut visited_scopes,
750 );
751
752 let mut scopes_storages = self.scopes_storages.borrow_mut();
753 let scope_storage = scopes_storages.get_mut(&scope_rc.borrow().id).unwrap();
754 scope_storage.current_value = 0;
755 scope_storage.current_run += 1;
756
757 scope_rc.borrow_mut().element = Some(path_element);
758 }
759
760 mutations
761 }
762
763 pub fn run_in<T>(&self, run: impl FnOnce() -> T) -> T {
764 CurrentContext::run(
765 CurrentContext {
766 scope_id: ScopeId::ROOT,
767 scopes_storages: self.scopes_storages.clone(),
768 tasks: self.tasks.clone(),
769 task_id_counter: self.task_id_counter.clone(),
770 sender: self.sender.clone(),
771 },
772 run,
773 )
774 }
775
776 #[cfg_attr(feature = "hotpath", hotpath::measure)]
777 fn run_scope(
778 &mut self,
779 scope: &Rc<RefCell<Scope>>,
780 element: &PathElement,
781 mutations: &mut Mutations,
782 visited_scopes: &mut FxHashSet<ScopeId>,
783 ) {
784 visited_scopes.insert(scope.borrow().id);
785 match element {
786 PathElement::Component {
787 comp,
788 props,
789 key,
790 path,
791 } => {
792 let assigned_scope_id = scope
794 .borrow()
795 .nodes
796 .get(path)
797 .and_then(|path_node| path_node.scope_id)
798 .unwrap();
799
800 let parent_node_id = if path.as_ref() == [0] {
801 scope.borrow().parent_node_id_in_parent
802 } else {
803 scope
804 .borrow()
805 .nodes
806 .get(&path[..path.len() - 1])
807 .unwrap()
808 .node_id
809 };
810
811 if let Some(Ok(mut existing_scope)) = self
812 .scopes
813 .get(&assigned_scope_id)
814 .map(|s| s.try_borrow_mut())
815 {
816 let key_changed = existing_scope.key != *key;
817 let type_changed = (existing_scope.props.as_ref() as &dyn Any).type_id()
819 != (props.as_ref() as &dyn Any).type_id();
820 if key_changed || type_changed || existing_scope.props.changed(props.as_ref()) {
821 self.dirty_scopes.insert(assigned_scope_id);
822 existing_scope.props = props.clone();
823
824 if key_changed || type_changed {
825 existing_scope.key = key.clone();
826 existing_scope.comp = comp.clone();
827 self.scopes_storages
828 .borrow_mut()
829 .get_mut(&assigned_scope_id)
830 .unwrap()
831 .reset();
832 }
833 }
834 } else {
835 self.scopes.insert(
836 assigned_scope_id,
837 Rc::new(RefCell::new(Scope {
838 parent_node_id_in_parent: parent_node_id,
839 path_in_parent: path.clone(),
840 height: scope.borrow().height + 1,
841 parent_id: Some(scope.borrow().id),
842 id: assigned_scope_id,
843 key: key.clone(),
844 comp: comp.clone(),
845 props: props.clone(),
846 element: None,
847 nodes: PathGraph::default(),
848 })),
849 );
850 self.scopes_storages.borrow_mut().insert(
851 assigned_scope_id,
852 ScopeStorage::new(Some(scope.borrow().id), |writer| {
853 ReactiveContext::new_for_scope(
854 self.sender.clone(),
855 assigned_scope_id,
856 writer,
857 )
858 }),
859 );
860 self.dirty_scopes.insert(assigned_scope_id);
861 }
862
863 let was_dirty = self.dirty_scopes.remove(&assigned_scope_id);
864
865 if !was_dirty {
866 return;
868 }
869
870 let scope_rc = self.scopes.get(&assigned_scope_id).cloned().unwrap();
871
872 let element = hotpath::measure_block!("Scope Rendering", {
873 CurrentContext::run_with_reactive(
874 CurrentContext {
875 scope_id: assigned_scope_id,
876 scopes_storages: self.scopes_storages.clone(),
877 tasks: self.tasks.clone(),
878 task_id_counter: self.task_id_counter.clone(),
879 sender: self.sender.clone(),
880 },
881 || {
882 let scope = scope_rc.borrow();
883 #[cfg(feature = "hotreload")]
884 {
885 subsecond::call(|| (scope.comp)(scope.props.clone()))
886 }
887 #[cfg(not(feature = "hotreload"))]
888 {
889 (scope.comp)(scope.props.clone())
890 }
891 },
892 )
893 });
894
895 let path_element = PathElement::from_element(vec![0], element);
896 let mut diff = Diff::default();
897 path_element.diff(scope_rc.borrow().element.as_ref(), &mut diff);
898
899 self.apply_diff(&scope_rc, diff, mutations, &path_element);
900
901 self.run_scope(&scope_rc, &path_element, mutations, visited_scopes);
902 let mut scopes_storages = self.scopes_storages.borrow_mut();
903 let scope_storage = scopes_storages.get_mut(&assigned_scope_id).unwrap();
904 scope_storage.current_value = 0;
905 scope_storage.current_run += 1;
906
907 scope_rc.borrow_mut().element = Some(path_element);
908 }
909 PathElement::Element { elements, .. } => {
910 for element in elements.iter() {
911 self.run_scope(scope, element, mutations, visited_scopes);
912 }
913 }
914 }
915 }
916
917 fn find_scope_root_parent_info(
920 &self,
921 parent_id: Option<ScopeId>,
922 parent_node_id: NodeId,
923 scope_id: ScopeId,
924 ) -> (NodeId, u32) {
925 let mut index_inside_parent = 0;
926
927 if let Some(parent_id) = parent_id {
928 let mut buffer = Some((parent_id, parent_node_id, scope_id));
929 while let Some((parent_id, parent_node_id, scope_id)) = buffer.take() {
930 let parent_scope = self.scopes.get(&parent_id).unwrap();
931 let parent_scope = parent_scope.borrow();
932
933 let scope = self.scopes.get(&scope_id).unwrap();
934 let scope = scope.borrow();
935
936 let path_node_parent = parent_scope.nodes.find(|v| {
937 if let Some(v) = v {
938 v.node_id == parent_node_id
939 } else {
940 false
941 }
942 });
943
944 if let Some(path_node_parent) = path_node_parent {
945 if let Some(scope_id) = path_node_parent.scope_id {
946 if let Some(parent_id) = parent_scope.parent_id {
947 buffer.replace((
949 parent_id,
950 parent_scope.parent_node_id_in_parent,
951 scope_id,
952 ));
953 } else {
954 assert_eq!(scope_id, ScopeId::ROOT);
955 }
956 } else {
957 index_inside_parent = *scope.path_in_parent.last().unwrap();
959 return (parent_node_id, index_inside_parent);
960 }
961 } else if let Some(new_parent_id) = parent_scope.parent_id {
962 buffer.replace((
964 new_parent_id,
965 parent_scope.parent_node_id_in_parent,
966 parent_id,
967 ));
968 }
969 }
970 } else {
971 assert_eq!(scope_id, ScopeId::ROOT);
972 }
973
974 (parent_node_id, index_inside_parent)
975 }
976
977 fn find_scope_root_node_id(&self, scope_id: ScopeId) -> NodeId {
981 let scope_rc = self.scopes.get(&scope_id).unwrap();
982 let scope = scope_rc.borrow();
983 let path_node = scope.nodes.get(&[0]).unwrap();
984 if let Some(child_scope_id) = path_node.scope_id {
985 self.find_scope_root_node_id(child_scope_id)
986 } else {
987 path_node.node_id
988 }
989 }
990
991 fn process_addition(
992 &mut self,
993 scope: &Rc<RefCell<Scope>>,
994 added: &[u32],
995 path_element: &PathElement,
996 mutations: &mut Mutations,
997 parents_to_resync_scopes: &mut FxHashSet<Box<[u32]>>,
998 ) {
999 let (parent_node_id, index_inside_parent) = if added == [0] {
1000 let parent_id = scope.borrow().parent_id;
1001 let scope_id = scope.borrow().id;
1002 let parent_node_id = scope.borrow().parent_node_id_in_parent;
1003 self.find_scope_root_parent_info(parent_id, parent_node_id, scope_id)
1004 } else {
1005 parents_to_resync_scopes.insert(Box::from(&added[..added.len() - 1]));
1006 (
1007 scope
1008 .borrow()
1009 .nodes
1010 .get(&added[..added.len() - 1])
1011 .unwrap()
1012 .node_id,
1013 added[added.len() - 1],
1014 )
1015 };
1016
1017 self.node_id_counter += 1;
1018
1019 path_element.with_element(added, |element| match element {
1020 PathElement::Component { .. } => {
1021 self.scope_id_counter += 1;
1022 let scope_id = self.scope_id_counter;
1023
1024 scope.borrow_mut().nodes.insert(
1025 added,
1026 PathNode {
1027 node_id: self.node_id_counter,
1028 scope_id: Some(scope_id),
1029 },
1030 );
1031 }
1032 PathElement::Element { element, .. } => {
1033 mutations.added.push(MutationAdd {
1034 node_id: self.node_id_counter,
1035 parent_id: parent_node_id,
1036 index: index_inside_parent,
1037 element: element.clone(),
1038 });
1039
1040 self.node_to_scope
1041 .insert(self.node_id_counter, scope.borrow().id);
1042 scope.borrow_mut().nodes.insert(
1043 added,
1044 PathNode {
1045 node_id: self.node_id_counter,
1046 scope_id: None,
1047 },
1048 );
1049 }
1050 });
1051 }
1052
1053 #[cfg_attr(feature = "hotpath", hotpath::measure)]
1054 fn apply_diff(
1055 &mut self,
1056 scope: &Rc<RefCell<Scope>>,
1057 diff: Diff,
1058 mutations: &mut Mutations,
1059 path_element: &PathElement,
1060 ) {
1061 let mut moved_nodes =
1062 FxHashMap::<Box<[u32]>, (NodeId, FxHashMap<u32, PathNode>)>::default();
1063 let mut parents_to_resync_scopes = FxHashSet::default();
1064
1065 for (parent, movements) in &diff.moved {
1068 parents_to_resync_scopes.insert(parent.clone());
1069 let old_parent = resolve_old_path(parent, &diff.moved);
1072 let paths = moved_nodes.entry(parent.clone()).or_insert_with(|| {
1073 let parent_node_id = scope.borrow().nodes.get(&old_parent).unwrap().node_id;
1074 (parent_node_id, FxHashMap::default())
1075 });
1076
1077 for (from, _to) in movements.iter() {
1078 let mut old_child_path = old_parent.clone();
1079 old_child_path.push(*from);
1080
1081 let path_node = scope.borrow().nodes.get(&old_child_path).cloned().unwrap();
1082
1083 paths.1.insert(*from, path_node);
1084 }
1085 }
1086
1087 let mut selected_roots: HashMap<&[u32], HashSet<&[u32]>> = HashMap::default();
1089 let mut scope_removal_buffer = vec![];
1090
1091 'remove: for removed in diff.removed.iter().sorted_by(|a, b| {
1111 for (x, y) in a.iter().zip(b.iter()) {
1112 match x.cmp(y) {
1113 Ordering::Equal => continue,
1114 non_eq => return non_eq.reverse(),
1115 }
1116 }
1117 b.len().cmp(&a.len())
1118 }) {
1119 parents_to_resync_scopes.insert(Box::from(&removed[..removed.len() - 1]));
1120
1121 let path_node = scope.borrow().nodes.get(removed).cloned();
1122 if let Some(PathNode { node_id, scope_id }) = path_node {
1123 if scope_id.is_none() {
1124 let index_inside_parent = if removed.as_ref() == [0] {
1125 let parent_id = scope.borrow().parent_id;
1126 let scope_id = scope.borrow().id;
1127 let parent_node_id = scope.borrow().parent_node_id_in_parent;
1128 self.find_scope_root_parent_info(parent_id, parent_node_id, scope_id)
1129 .1
1130 } else {
1131 removed[removed.len() - 1]
1133 };
1134
1135 mutations.removed.push(MutationRemove::Element {
1137 id: node_id,
1138 index: index_inside_parent,
1139 });
1140 }
1141
1142 for (root, inner) in &mut selected_roots {
1144 if is_descendant(removed, root) {
1145 inner.insert(removed);
1146 continue 'remove;
1147 }
1148 }
1149
1150 selected_roots.retain(|root, _| !is_descendant(root, removed));
1152
1153 selected_roots
1154 .entry(&removed[..removed.len() - 1])
1155 .or_default()
1156 .insert(removed);
1157 } else {
1158 unreachable!()
1159 }
1160 }
1161
1162 for (root, removed) in selected_roots.iter().sorted_by(|(a, _), (b, _)| {
1164 for (x, y) in a.iter().zip(b.iter()) {
1165 match x.cmp(y) {
1166 Ordering::Equal => continue,
1167 non_eq => return non_eq.reverse(),
1168 }
1169 }
1170 b.len().cmp(&a.len())
1171 }) {
1172 scope.borrow_mut().nodes.retain(
1173 root,
1174 |p, _| !removed.contains(p),
1175 |_, &PathNode { scope_id, node_id }| {
1176 if let Some(scope_id) = scope_id {
1177 scope_removal_buffer.push(self.scopes.get(&scope_id).cloned().unwrap());
1179 } else {
1180 self.node_to_scope.remove(&node_id).unwrap();
1181 }
1182 },
1183 );
1184 }
1185
1186 let mut scope_removal_queue = VecDeque::new();
1187
1188 while let Some(scope_rc) = scope_removal_buffer.pop() {
1189 scope_removal_queue.push_front(scope_rc.clone());
1192
1193 let scope = scope_rc.borrow_mut();
1194
1195 let mut scope_root_node_id = None;
1196
1197 scope
1199 .nodes
1200 .traverse(&[], |path, &PathNode { scope_id, node_id }| {
1201 if let Some(scope_id) = scope_id {
1202 scope_removal_buffer.push(self.scopes.get(&scope_id).cloned().unwrap());
1203 } else {
1204 self.node_to_scope.remove(&node_id).unwrap();
1205 }
1206 if path == [0] {
1207 scope_root_node_id = Some(node_id);
1208 }
1209 });
1210
1211 mutations.removed.push(MutationRemove::Scope {
1214 id: scope_root_node_id.unwrap(),
1215 });
1216 }
1217
1218 for scope_rc in scope_removal_queue {
1220 let scope = scope_rc.borrow_mut();
1221
1222 self.scopes.remove(&scope.id);
1223
1224 CurrentContext::run_with_reactive(
1226 CurrentContext {
1227 scope_id: scope.id,
1228 scopes_storages: self.scopes_storages.clone(),
1229 tasks: self.tasks.clone(),
1230 task_id_counter: self.task_id_counter.clone(),
1231 sender: self.sender.clone(),
1232 },
1233 || {
1234 let mut _removed_tasks = Vec::new();
1236
1237 self.tasks.borrow_mut().retain(|task_id, task| {
1238 if task.borrow().scope_id == scope.id {
1239 _removed_tasks.push((*task_id, task.clone()));
1240 false
1241 } else {
1242 true
1243 }
1244 });
1245 drop(_removed_tasks);
1246 let _scope = self.scopes_storages.borrow_mut().remove(&scope.id);
1248 },
1249 );
1250 }
1251
1252 let mut deferred_adds = Vec::new();
1275
1276 for added in diff
1277 .added
1278 .iter()
1279 .sorted_by(|a, b| {
1280 for (x, y) in a.iter().zip(b.iter()) {
1281 match x.cmp(y) {
1282 Ordering::Equal => continue,
1283 non_eq => return non_eq.reverse(),
1284 }
1285 }
1286 b.len().cmp(&a.len())
1287 })
1288 .rev()
1289 {
1290 let parent = &added[..added.len() - 1];
1291 let has_moved_ancestor = resolve_old_path(parent, &diff.moved) != *parent;
1292 if has_moved_ancestor {
1293 deferred_adds.push(added.clone());
1294 continue;
1295 }
1296
1297 self.process_addition(
1298 scope,
1299 added,
1300 path_element,
1301 mutations,
1302 &mut parents_to_resync_scopes,
1303 );
1304 }
1305
1306 for (parent, movements) in diff.moved.into_iter().sorted_by(|(a, _), (b, _)| {
1307 for (x, y) in a.iter().zip(b.iter()) {
1308 match x.cmp(y) {
1309 Ordering::Equal => continue,
1310 non_eq => return non_eq.reverse(),
1311 }
1312 }
1313 a.len().cmp(&b.len())
1314 }) {
1315 parents_to_resync_scopes.insert(parent.clone());
1316
1317 let (parent_node_id, paths) = moved_nodes.get_mut(&parent).unwrap();
1318
1319 for (from, to) in movements.into_iter().sorted_by_key(|e| e.1) {
1320 let path_node = paths.remove(&from).unwrap();
1321
1322 let PathNode { node_id, scope_id } = path_node;
1323
1324 let from_path = scope
1326 .borrow()
1327 .nodes
1328 .find_child_path(&parent, |v| v == Some(&path_node))
1329 .unwrap();
1330
1331 let mut to_path = parent.to_vec();
1332 to_path.push(to);
1333
1334 if from_path == to_path {
1335 continue;
1336 }
1337
1338 let path_entry = scope.borrow_mut().nodes.remove(&from_path).unwrap();
1340 scope.borrow_mut().nodes.insert_entry(&to_path, path_entry);
1341
1342 if let Some(scope_id) = scope_id {
1343 let scope_root_node_id = self.find_scope_root_node_id(scope_id);
1344 let scope_rc = self.scopes.get(&scope_id).cloned().unwrap();
1345 let scope = scope_rc.borrow();
1346
1347 mutations
1349 .moved
1350 .entry(scope.parent_node_id_in_parent)
1351 .or_default()
1352 .push(MutationMove {
1353 index: to,
1354 node_id: scope_root_node_id,
1355 });
1356 } else {
1357 mutations
1359 .moved
1360 .entry(*parent_node_id)
1361 .or_default()
1362 .push(MutationMove { index: to, node_id });
1363 }
1364 }
1365 }
1366
1367 for added in &deferred_adds {
1369 self.process_addition(
1370 scope,
1371 added,
1372 path_element,
1373 mutations,
1374 &mut parents_to_resync_scopes,
1375 );
1376 }
1377
1378 for (modified, flags) in diff.modified {
1379 path_element.with_element(&modified, |element| match element {
1380 PathElement::Component { .. } => {
1381 unreachable!()
1383 }
1384 PathElement::Element { element, .. } => {
1385 let node_id = scope
1386 .borrow()
1387 .nodes
1388 .get(&modified)
1389 .map(|path_node| path_node.node_id)
1390 .unwrap();
1391 mutations.modified.push(MutationModified {
1392 node_id,
1393 element: element.clone(),
1394 flags,
1395 });
1396 }
1397 });
1398 }
1399
1400 for parent in parents_to_resync_scopes {
1403 if diff.added.contains(&parent) {
1406 continue;
1408 }
1409
1410 scope
1412 .borrow_mut()
1413 .nodes
1414 .traverse_1_level(&parent, |p, path_node| {
1415 if let Some(scope_id) = path_node.scope_id
1416 && let Some(scope_rc) = self.scopes.get(&scope_id)
1417 {
1418 let mut scope = scope_rc.borrow_mut();
1419 scope.path_in_parent = Box::from(p);
1420 }
1421 });
1422 }
1423 }
1424
1425 pub fn reload(&mut self) {
1430 self.tasks.borrow_mut().clear();
1431 self.dirty_tasks.clear();
1432 while self.receiver.try_recv().is_ok() {}
1433
1434 let scopes = self
1435 .scopes
1436 .iter()
1437 .sorted_by_key(|(_, s)| s.borrow().height)
1438 .map(|(_, s)| s.borrow().id)
1439 .collect::<Vec<_>>();
1440
1441 for scope_id in scopes {
1442 CurrentContext::run(
1443 CurrentContext {
1444 scope_id,
1445 scopes_storages: self.scopes_storages.clone(),
1446 tasks: self.tasks.clone(),
1447 task_id_counter: self.task_id_counter.clone(),
1448 sender: self.sender.clone(),
1449 },
1450 || {
1451 let _hooks = self
1452 .scopes_storages
1453 .borrow_mut()
1454 .get_mut(&scope_id)
1455 .map(|storage| storage.reset_hooks());
1456 },
1457 );
1458 }
1459
1460 self.dirty_scopes.extend(self.scopes.keys());
1461 let _ = self
1462 .sender
1463 .unbounded_send(Message::MarkScopeAsDirty(ScopeId::ROOT));
1464 }
1465}
1466
1467#[derive(Default, Debug)]
1468pub struct Diff {
1469 pub added: Vec<Box<[u32]>>,
1470
1471 pub modified: Vec<(Box<[u32]>, DiffModifies)>,
1472
1473 pub removed: Vec<Box<[u32]>>,
1474
1475 pub moved: HashMap<Box<[u32]>, Vec<(u32, u32)>>,
1476}
1477
1478fn resolve_old_path(new_path: &[u32], moved: &HashMap<Box<[u32]>, Vec<(u32, u32)>>) -> Vec<u32> {
1483 let mut old_path = Vec::with_capacity(new_path.len());
1484 for i in 0..new_path.len() {
1485 let new_parent = &new_path[..i];
1486 let new_index = new_path[i];
1487 if let Some(movements) = moved.get(new_parent)
1488 && let Some(&(from, _)) = movements.iter().find(|(_, to)| *to == new_index)
1489 {
1490 old_path.push(from);
1491 continue;
1492 }
1493 old_path.push(new_index);
1494 }
1495 old_path
1496}
1497
1498fn is_descendant(candidate: &[u32], ancestor: &[u32]) -> bool {
1499 if ancestor.len() > candidate.len() {
1500 return false;
1501 }
1502 candidate[..ancestor.len()] == *ancestor
1503}