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