Merge branch 'master' of github.com:DioxusLabs/dioxus
This commit is contained in:
commit
ddd4875e13
4 files changed
+76
-23
No files matched your search
@@ -24,7 +24,8 @@ jobs:
|
|||||||
run: cd docs &&
|
run: cd docs &&
|
||||||
cd guide && mdbook build -d ../nightly/guide && cd .. &&
|
cd guide && mdbook build -d ../nightly/guide && cd .. &&
|
||||||
cd reference && mdbook build -d ../nightly/reference && cd .. &&
|
cd reference && mdbook build -d ../nightly/reference && cd .. &&
|
||||||
cd router && mdbook build -d ../nightly/router && cd ..
|
cd router && mdbook build -d ../nightly/router && cd .. &&
|
||||||
|
cd cli && mdbook build -d ../nightly/cli && cd ..
|
||||||
|
|
||||||
- name: Deploy 🚀
|
- name: Deploy 🚀
|
||||||
uses: JamesIves/github-pages-deploy-action@v4.2.3
|
uses: JamesIves/github-pages-deploy-action@v4.2.3
|
||||||
|
|||||||
@@ -813,6 +813,15 @@ impl<'b> DiffState<'b> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// remove any old children that are not shared
|
||||||
|
// todo: make this an iterator
|
||||||
|
for child in old {
|
||||||
|
let key = child.key().unwrap();
|
||||||
|
if !shared_keys.contains(&key) {
|
||||||
|
self.remove_nodes([child], true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 4. Compute the LIS of this list
|
// 4. Compute the LIS of this list
|
||||||
let mut lis_sequence = Vec::default();
|
let mut lis_sequence = Vec::default();
|
||||||
lis_sequence.reserve(new_index_to_old_index.len());
|
lis_sequence.reserve(new_index_to_old_index.len());
|
||||||
|
|||||||
+60
-19
@@ -7,7 +7,7 @@ use std::{
|
|||||||
any::{Any, TypeId},
|
any::{Any, TypeId},
|
||||||
borrow::Borrow,
|
borrow::Borrow,
|
||||||
cell::{Cell, RefCell},
|
cell::{Cell, RefCell},
|
||||||
collections::HashMap,
|
collections::{HashMap, HashSet},
|
||||||
future::Future,
|
future::Future,
|
||||||
pin::Pin,
|
pin::Pin,
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
@@ -68,7 +68,12 @@ impl ScopeArena {
|
|||||||
heuristics: RefCell::new(FxHashMap::default()),
|
heuristics: RefCell::new(FxHashMap::default()),
|
||||||
free_scopes: RefCell::new(Vec::new()),
|
free_scopes: RefCell::new(Vec::new()),
|
||||||
nodes: RefCell::new(nodes),
|
nodes: RefCell::new(nodes),
|
||||||
tasks: TaskQueue::new(sender),
|
tasks: Rc::new(TaskQueue {
|
||||||
|
tasks: RefCell::new(FxHashMap::default()),
|
||||||
|
task_map: RefCell::new(FxHashMap::default()),
|
||||||
|
gen: Cell::new(0),
|
||||||
|
sender,
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -179,6 +184,15 @@ impl ScopeArena {
|
|||||||
log::trace!("removing scope {:?}", id);
|
log::trace!("removing scope {:?}", id);
|
||||||
self.ensure_drop_safety(id);
|
self.ensure_drop_safety(id);
|
||||||
|
|
||||||
|
// Dispose of any ongoing tasks
|
||||||
|
let mut tasks = self.tasks.tasks.borrow_mut();
|
||||||
|
let mut task_map = self.tasks.task_map.borrow_mut();
|
||||||
|
if let Some(cur_tasks) = task_map.remove(&id) {
|
||||||
|
for task in cur_tasks {
|
||||||
|
tasks.remove(&task);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Safety:
|
// Safety:
|
||||||
// - ensure_drop_safety ensures that no references to this scope are in use
|
// - ensure_drop_safety ensures that no references to this scope are in use
|
||||||
// - this raw pointer is removed from the map
|
// - this raw pointer is removed from the map
|
||||||
@@ -435,7 +449,13 @@ pub struct ScopeId(pub usize);
|
|||||||
/// once a Task has been completed.
|
/// once a Task has been completed.
|
||||||
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
|
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||||
pub struct TaskId(pub usize);
|
pub struct TaskId {
|
||||||
|
/// The global ID of the task
|
||||||
|
pub id: usize,
|
||||||
|
|
||||||
|
/// The original scope that this task was scheduled in
|
||||||
|
pub scope: ScopeId,
|
||||||
|
}
|
||||||
|
|
||||||
/// Every component in Dioxus is represented by a `ScopeState`.
|
/// Every component in Dioxus is represented by a `ScopeState`.
|
||||||
///
|
///
|
||||||
@@ -745,7 +765,7 @@ impl ScopeState {
|
|||||||
.unbounded_send(SchedulerMsg::NewTask(self.our_arena_idx))
|
.unbounded_send(SchedulerMsg::NewTask(self.our_arena_idx))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
self.tasks.push_fut(fut)
|
self.tasks.spawn(self.our_arena_idx, fut)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Spawns the future but does not return the TaskId
|
/// Spawns the future but does not return the TaskId
|
||||||
@@ -753,10 +773,24 @@ impl ScopeState {
|
|||||||
self.push_future(fut);
|
self.push_future(fut);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Spawn a future that Dioxus will never clean up
|
||||||
|
///
|
||||||
|
/// This is good for tasks that need to be run after the component has been dropped.
|
||||||
|
pub fn spawn_forever(&self, fut: impl Future<Output = ()> + 'static) -> TaskId {
|
||||||
|
// wake up the scheduler if it is sleeping
|
||||||
|
self.tasks
|
||||||
|
.sender
|
||||||
|
.unbounded_send(SchedulerMsg::NewTask(self.our_arena_idx))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// The root scope will never be unmounted so we can just add the task at the top of the app
|
||||||
|
self.tasks.spawn(ScopeId(0), fut)
|
||||||
|
}
|
||||||
|
|
||||||
/// Informs the scheduler that this task is no longer needed and should be removed
|
/// Informs the scheduler that this task is no longer needed and should be removed
|
||||||
/// on next poll.
|
/// on next poll.
|
||||||
pub fn remove_future(&self, id: TaskId) {
|
pub fn remove_future(&self, id: TaskId) {
|
||||||
self.tasks.remove_fut(id);
|
self.tasks.remove(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Take a lazy VNode structure and actually build it with the context of the VDom's efficient VNode allocator.
|
/// Take a lazy VNode structure and actually build it with the context of the VDom's efficient VNode allocator.
|
||||||
@@ -940,36 +974,43 @@ impl BumpFrame {
|
|||||||
|
|
||||||
pub(crate) struct TaskQueue {
|
pub(crate) struct TaskQueue {
|
||||||
pub(crate) tasks: RefCell<FxHashMap<TaskId, InnerTask>>,
|
pub(crate) tasks: RefCell<FxHashMap<TaskId, InnerTask>>,
|
||||||
|
pub(crate) task_map: RefCell<FxHashMap<ScopeId, HashSet<TaskId>>>,
|
||||||
gen: Cell<usize>,
|
gen: Cell<usize>,
|
||||||
sender: UnboundedSender<SchedulerMsg>,
|
sender: UnboundedSender<SchedulerMsg>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) type InnerTask = Pin<Box<dyn Future<Output = ()>>>;
|
pub(crate) type InnerTask = Pin<Box<dyn Future<Output = ()>>>;
|
||||||
impl TaskQueue {
|
impl TaskQueue {
|
||||||
fn new(sender: UnboundedSender<SchedulerMsg>) -> Rc<Self> {
|
fn spawn(&self, scope: ScopeId, task: impl Future<Output = ()> + 'static) -> TaskId {
|
||||||
Rc::new(Self {
|
|
||||||
tasks: RefCell::new(FxHashMap::default()),
|
|
||||||
gen: Cell::new(0),
|
|
||||||
sender,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
fn push_fut(&self, task: impl Future<Output = ()> + 'static) -> TaskId {
|
|
||||||
let pinned = Box::pin(task);
|
let pinned = Box::pin(task);
|
||||||
let id = self.gen.get();
|
let id = self.gen.get();
|
||||||
self.gen.set(id + 1);
|
self.gen.set(id + 1);
|
||||||
let tid = TaskId(id);
|
let tid = TaskId { id, scope };
|
||||||
|
|
||||||
self.tasks.borrow_mut().insert(tid, pinned);
|
self.tasks.borrow_mut().insert(tid, pinned);
|
||||||
|
|
||||||
|
// also add to the task map
|
||||||
|
// when the component is unmounted we know to remove it from the map
|
||||||
|
self.task_map
|
||||||
|
.borrow_mut()
|
||||||
|
.entry(scope)
|
||||||
|
.or_default()
|
||||||
|
.insert(tid);
|
||||||
|
|
||||||
tid
|
tid
|
||||||
}
|
}
|
||||||
fn remove_fut(&self, id: TaskId) {
|
|
||||||
|
fn remove(&self, id: TaskId) {
|
||||||
if let Ok(mut tasks) = self.tasks.try_borrow_mut() {
|
if let Ok(mut tasks) = self.tasks.try_borrow_mut() {
|
||||||
let _ = tasks.remove(&id);
|
let _ = tasks.remove(&id);
|
||||||
} else {
|
}
|
||||||
// todo: it should be okay to remote a fut while the queue is being polled
|
|
||||||
// However, it's not currently possible to do that.
|
// the task map is still around, but it'll be removed when the scope is unmounted
|
||||||
log::trace!("Unable to remove task from task queue. This is probably a bug.");
|
if let Some(task_map) = self.task_map.borrow_mut().get_mut(&id.scope) {
|
||||||
|
task_map.remove(&id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn has_tasks(&self) -> bool {
|
pub(crate) fn has_tasks(&self) -> bool {
|
||||||
!self.tasks.borrow().is_empty()
|
!self.tasks.borrow().is_empty()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -623,16 +623,17 @@ fn controlled_keyed_diffing_out_of_order() {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
changes.edits,
|
changes.edits,
|
||||||
[
|
[
|
||||||
|
Remove { root: 4 },
|
||||||
// move 4 to after 6
|
// move 4 to after 6
|
||||||
PushRoot { root: 1 },
|
PushRoot { root: 1 },
|
||||||
InsertAfter { n: 1, root: 3 },
|
InsertAfter { n: 1, root: 3 },
|
||||||
// remove 7
|
// remove 7
|
||||||
|
|
||||||
// create 9 and insert before 6
|
// create 9 and insert before 6
|
||||||
CreateElement { root: 5, tag: "div" },
|
CreateElement { root: 4, tag: "div" },
|
||||||
InsertBefore { n: 1, root: 3 },
|
InsertBefore { n: 1, root: 3 },
|
||||||
// create 0 and insert before 5
|
// create 0 and insert before 5
|
||||||
CreateElement { root: 6, tag: "div" },
|
CreateElement { root: 5, tag: "div" },
|
||||||
InsertBefore { n: 1, root: 2 },
|
InsertBefore { n: 1, root: 2 },
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
@@ -659,7 +660,8 @@ fn controlled_keyed_diffing_out_of_order_max_test() {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
changes.edits,
|
changes.edits,
|
||||||
[
|
[
|
||||||
CreateElement { root: 6, tag: "div" },
|
Remove { root: 5 },
|
||||||
|
CreateElement { root: 5, tag: "div" },
|
||||||
InsertBefore { n: 1, root: 3 },
|
InsertBefore { n: 1, root: 3 },
|
||||||
PushRoot { root: 4 },
|
PushRoot { root: 4 },
|
||||||
InsertBefore { n: 1, root: 1 },
|
InsertBefore { n: 1, root: 1 },
|
||||||
|
|||||||
Reference in new issue
Block a user