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