Merge branch 'master' into jk/fix-comments-on-exprs

This commit is contained in:
Jonathan Kelley committed 2023-01-10 16:57:33 -08:00
commit 6500a6d490
66 files changed
+998 -651

No files matched your search

+12 -3
View File
@@ -1,9 +1,9 @@
use crate::nodes::RenderReturn;
use bumpalo::Bump;
use std::cell::Cell;
use std::cell::{Cell, UnsafeCell};
pub(crate) struct BumpFrame {
pub bump: Bump,
pub bump: UnsafeCell<Bump>,
pub node: Cell<*const RenderReturn<'static>>,
}
@@ -11,7 +11,7 @@ impl BumpFrame {
pub(crate) fn new(capacity: usize) -> Self {
let bump = Bump::with_capacity(capacity);
Self {
bump,
bump: UnsafeCell::new(bump),
node: Cell::new(std::ptr::null()),
}
}
@@ -26,4 +26,13 @@ impl BumpFrame {
unsafe { std::mem::transmute(&*node) }
}
pub(crate) fn bump(&self) -> &Bump {
unsafe { &*self.bump.get() }
}
#[allow(clippy::mut_from_ref)]
pub(crate) unsafe fn bump_mut(&self) -> &mut Bump {
unsafe { &mut *self.bump.get() }
}
}
+25 -21
View File
@@ -81,9 +81,7 @@ impl<'b> VirtualDom {
// The best renderers will have templates prehydrated and registered
// Just in case, let's create the template using instructions anyways
if !self.templates.contains_key(&node.template.get().name) {
self.register_template(node.template.get());
}
self.register_template(node.template.get());
// we know that this will generate at least one mutation per node
self.mutations
@@ -389,30 +387,36 @@ impl<'b> VirtualDom {
/// Insert a new template into the VirtualDom's template registry
pub(crate) fn register_template(&mut self, mut template: Template<'static>) {
// First, make sure we mark the template as seen, regardless if we process it
let (path, byte_index) = template.name.rsplit_once(':').unwrap();
let byte_index = byte_index.parse::<usize>().unwrap();
// if hot reloading is enabled, then we need to check for a template that has overriten this one
#[cfg(debug_assertions)]
if let Some(mut new_template) = self
// First, check if we've already seen this template
if self
.templates
.get_mut(path)
.and_then(|map| map.remove(&usize::MAX))
.get(&path)
.filter(|set| set.contains_key(&byte_index))
.is_none()
{
// the byte index of the hot reloaded template could be different
new_template.name = template.name;
template = new_template;
}
// if hot reloading is enabled, then we need to check for a template that has overriten this one
#[cfg(debug_assertions)]
if let Some(mut new_template) = self
.templates
.get_mut(path)
.and_then(|map| map.remove(&usize::MAX))
{
// the byte index of the hot reloaded template could be different
new_template.name = template.name;
template = new_template;
}
self.templates
.entry(path)
.or_default()
.insert(byte_index, template);
self.templates
.entry(path)
.or_default()
.insert(byte_index, template);
// If it's all dynamic nodes, then we don't need to register it
if !template.is_completely_dynamic() {
self.mutations.templates.push(template);
// If it's all dynamic nodes, then we don't need to register it
if !template.is_completely_dynamic() {
self.mutations.templates.push(template);
}
}
}
-1
View File
@@ -819,7 +819,6 @@ impl<'a, 'b> IntoTemplate<'a> for LazyNodes<'a, 'b> {
}
// Note that we're using the E as a generic but this is never crafted anyways.
#[doc(hidden)]
pub struct FromNodeIterator;
impl<'a, T, I> IntoDynNode<'a, FromNodeIterator> for T
where
+4 -3
View File
@@ -31,7 +31,8 @@ impl VirtualDom {
// If the task completes...
if task.task.borrow_mut().as_mut().poll(&mut cx).is_ready() {
// Remove it from the scope so we dont try to double drop it when the scope dropes
self.scopes[task.scope.0].spawned_tasks.remove(&id);
let scope = &self.scopes[task.scope.0];
scope.spawned_tasks.borrow_mut().remove(&id);
// Remove it from the scheduler
tasks.try_remove(id.0);
@@ -63,10 +64,10 @@ impl VirtualDom {
if let Poll::Ready(new_nodes) = as_pinned_mut.poll_unpin(&mut cx) {
let fiber = self.acquire_suspense_boundary(leaf.scope_id);
let scope = &mut self.scopes[scope_id.0];
let scope = &self.scopes[scope_id.0];
let arena = scope.current_frame();
let ret = arena.bump.alloc(match new_nodes {
let ret = arena.bump().alloc(match new_nodes {
Some(new) => RenderReturn::Ready(new),
None => RenderReturn::default(),
});
+7 -16
View File
@@ -7,7 +7,6 @@ use crate::{
scopes::{ScopeId, ScopeState},
virtual_dom::VirtualDom,
};
use bumpalo::Bump;
use futures_util::FutureExt;
use std::{
mem,
@@ -48,11 +47,10 @@ impl VirtualDom {
}))
}
fn acquire_current_scope_raw(&mut self) -> Option<*mut ScopeState> {
self.scope_stack
.last()
.copied()
.and_then(|id| self.scopes.get_mut(id.0).map(|f| f.as_mut() as *mut _))
fn acquire_current_scope_raw(&self) -> Option<*const ScopeState> {
let id = self.scope_stack.last().copied()?;
let scope = self.scopes.get(id.0)?;
Some(scope.as_ref())
}
pub(crate) fn run_scope(&mut self, scope_id: ScopeId) -> &RenderReturn {
@@ -62,17 +60,10 @@ impl VirtualDom {
self.ensure_drop_safety(scope_id);
let mut new_nodes = unsafe {
let scope = self.scopes[scope_id.0].as_mut();
self.scopes[scope_id.0].previous_frame().bump_mut().reset();
// if this frame hasn't been intialized yet, we can guess the size of the next frame to be more efficient
if scope.previous_frame().bump.allocated_bytes() == 0 {
scope.previous_frame_mut().bump =
Bump::with_capacity(scope.current_frame().bump.allocated_bytes());
} else {
scope.previous_frame_mut().bump.reset();
}
let scope = &self.scopes[scope_id.0];
// Make sure to reset the hook counter so we give out hooks in the right order
scope.hook_idx.set(0);
// safety: due to how we traverse the tree, we know that the scope is not currently aliased
@@ -142,7 +133,7 @@ impl VirtualDom {
let frame = scope.previous_frame();
// set the new head of the bump frame
let allocated = &*frame.bump.alloc(new_nodes);
let allocated = &*frame.bump().alloc(new_nodes);
frame.node.set(allocated);
// And move the render generation forward by one
+4 -12
View File
@@ -73,7 +73,7 @@ pub struct ScopeState {
pub(crate) node_arena_1: BumpFrame,
pub(crate) node_arena_2: BumpFrame,
pub(crate) parent: Option<*mut ScopeState>,
pub(crate) parent: Option<*const ScopeState>,
pub(crate) id: ScopeId,
pub(crate) height: u32,
@@ -85,7 +85,7 @@ pub struct ScopeState {
pub(crate) shared_contexts: RefCell<FxHashMap<TypeId, Box<dyn Any>>>,
pub(crate) tasks: Rc<Scheduler>,
pub(crate) spawned_tasks: FxHashSet<TaskId>,
pub(crate) spawned_tasks: RefCell<FxHashSet<TaskId>>,
pub(crate) borrowed_props: RefCell<Vec<*const VComponent<'static>>>,
pub(crate) attributes_to_drop: RefCell<Vec<*const Attribute<'static>>>,
@@ -111,14 +111,6 @@ impl<'src> ScopeState {
}
}
pub(crate) fn previous_frame_mut(&mut self) -> &mut BumpFrame {
match self.render_cnt.get() % 2 {
1 => &mut self.node_arena_1,
0 => &mut self.node_arena_2,
_ => unreachable!(),
}
}
/// Get the name of this component
pub fn name(&self) -> &str {
self.name
@@ -139,7 +131,7 @@ impl<'src> ScopeState {
/// If you need to allocate items that need to be dropped, use bumpalo's box.
pub fn bump(&self) -> &Bump {
// note that this is actually the previous frame since we use that as scratch space while the component is rendering
&self.previous_frame().bump
self.previous_frame().bump()
}
/// Get a handle to the currently active head node arena for this Scope
@@ -556,7 +548,7 @@ impl<'src> ScopeState {
#[allow(clippy::mut_from_ref)]
pub fn use_hook<State: 'static>(&self, initializer: impl FnOnce() -> State) -> &mut State {
let cur_hook = self.hook_idx.get();
let mut hook_list = self.hook_list.borrow_mut();
let mut hook_list = self.hook_list.try_borrow_mut().expect("The hook list is already borrowed: This error is likely caused by trying to use a hook inside a hook which violates the rules of hooks.");
if cur_hook >= hook_list.len() {
hook_list.push(self.hook_arena.alloc(initializer()));
+1 -1
View File
@@ -549,7 +549,7 @@ impl VirtualDom {
loop {
// first, unload any complete suspense trees
for finished_fiber in self.finished_fibers.drain(..) {
let scope = &mut self.scopes[finished_fiber.0];
let scope = &self.scopes[finished_fiber.0];
let context = scope.has_context::<Rc<SuspenseContext>>().unwrap();
self.mutations
+40 -34
View File
@@ -5,42 +5,48 @@ use std::future::IntoFuture;
use std::rc::Rc;
use std::time::Duration;
#[tokio::test]
async fn it_works() {
let mut dom = VirtualDom::new(app);
{
let mutations = dom.rebuild().santize();
// We should at least get the top-level template in before pausing for the children
// note: we dont test template edits anymore
// assert_eq!(
// mutations.templates,
// [
// CreateElement { name: "div" },
// CreateStaticText { value: "Waiting for child..." },
// CreateStaticPlaceholder,
// AppendChildren { m: 2 },
// SaveTemplate { name: "template", m: 1 }
// ]
// );
// And we should load it in and assign the placeholder properly
assert_eq!(
mutations.edits,
[
LoadTemplate { name: "template", index: 0, id: ElementId(1) },
// hmmmmmmmmm.... with suspense how do we guarantee that IDs increase linearly?
// can we even?
AssignId { path: &[1], id: ElementId(3) },
AppendChildren { m: 1, id: ElementId(0) },
]
);
}
#[test]
fn it_works() {
// wait just a moment, not enough time for the boundary to resolve
dom.wait_for_work().await;
tokio::runtime::Builder::new_current_thread()
.enable_time()
.build()
.unwrap()
.block_on(async {
let mut dom = VirtualDom::new(app);
{
let mutations = dom.rebuild().santize();
// We should at least get the top-level template in before pausing for the children
// note: we dont test template edits anymore
// assert_eq!(
// mutations.templates,
// [
// CreateElement { name: "div" },
// CreateStaticText { value: "Waiting for child..." },
// CreateStaticPlaceholder,
// AppendChildren { m: 2 },
// SaveTemplate { name: "template", m: 1 }
// ]
// );
// And we should load it in and assign the placeholder properly
assert_eq!(
mutations.edits,
[
LoadTemplate { name: "template", index: 0, id: ElementId(1) },
// hmmmmmmmmm.... with suspense how do we guarantee that IDs increase linearly?
// can we even?
AssignId { path: &[1], id: ElementId(3) },
AppendChildren { m: 1, id: ElementId(0) },
]
);
}
dom.wait_for_work().await;
});
}
fn app(cx: Scope) -> Element {
+1 -1
View File
@@ -9,7 +9,7 @@ repository = "https://github.com/DioxusLabs/dioxus/"
homepage = "https://dioxuslabs.com"
documentation = "https://dioxuslabs.com"
keywords = ["dom", "ui", "gui", "react", "wasm"]
rust-version = "1.60.0"
rust-version = "1.65.0"
[dependencies]
dioxus-core = { path = "../core", version = "^0.3.0" }
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "fermi"
version = "0.2.1"
version = "0.3.0"
authors = ["Jonathan Kelley"]
edition = "2018"
description = "Global state management for Dioxus"
+10
View File
@@ -4,6 +4,7 @@ use crate::events::{
};
use crate::geometry::{ClientPoint, Coordinates, ElementPoint, PagePoint, ScreenPoint};
use crate::input_data::{decode_key_location, decode_mouse_button_set, MouseButton};
use crate::DragData;
use keyboard_types::{Code, Key, Modifiers};
use std::convert::TryInto;
use std::str::FromStr;
@@ -40,6 +41,7 @@ uncheck_convert![
CompositionEvent => CompositionData,
KeyboardEvent => KeyboardData,
MouseEvent => MouseData,
MouseEvent => DragData,
TouchEvent => TouchData,
PointerEvent => PointerData,
WheelEvent => WheelData,
@@ -117,6 +119,14 @@ impl From<&MouseEvent> for MouseData {
}
}
impl From<&MouseEvent> for DragData {
fn from(value: &MouseEvent) -> Self {
Self {
mouse: MouseData::from(value),
}
}
}
impl From<&TouchEvent> for TouchData {
fn from(e: &TouchEvent) -> Self {
Self {
-3
View File
@@ -144,8 +144,6 @@ class Interpreter {
this.nodes[root].textContent = text;
}
SetAttribute(id, field, value, ns) {
console.log("set attribute", id, field, value, ns);
if (value === null) {
this.RemoveAttribute(id, field, ns);
}
@@ -228,7 +226,6 @@ class Interpreter {
}
MakeTemplateNode(node) {
console.log("making template node", node);
switch (node.type) {
case "Text":
return document.createTextNode(node.text);
+11 -2
View File
@@ -12,7 +12,13 @@ class IPC {
let ws = new WebSocket(WS_ADDR);
function ping() {
ws.send("__ping__");
}
ws.onopen = () => {
// we ping every 30 seconds to keep the websocket alive
setInterval(ping, 30000);
ws.send(serializeIpcMessage("initialize"));
};
@@ -21,8 +27,11 @@ class IPC {
};
ws.onmessage = (event) => {
let edits = JSON.parse(event.data);
window.interpreter.handleEdits(edits);
// Ignore pongs
if (event.data != "__pong__") {
let edits = JSON.parse(event.data);
window.interpreter.handleEdits(edits);
}
};
this.ws = ws;
+6 -2
View File
@@ -127,9 +127,13 @@ where
_ = vdom.wait_for_work() => {}
evt = ws.next() => {
match evt {
match evt.as_ref().map(|o| o.as_deref()) {
// respond with a pong every ping to keep the websocket alive
Some(Ok("__ping__")) => {
ws.send("__pong__".to_string()).await?;
}
Some(Ok(evt)) => {
if let Ok(IpcMessage { params }) = serde_json::from_str::<IpcMessage>(&evt) {
if let Ok(IpcMessage { params }) = serde_json::from_str::<IpcMessage>(evt) {
vdom.handle_event(&params.name, params.data.into_any(), params.element, params.bubbles);
}
}
+1 -1
View File
@@ -12,7 +12,7 @@ fn app(cx: Scope) -> Element {
width: "100%",
height: "100%",
flex_direction: "column",
onwheel: move |evt| alpha.set((**alpha + evt.inner().delta().strip_units().y as i64).min(100).max(0)),
onwheel: move |evt| alpha.set((**alpha + evt.inner().delta().strip_units().y as i64).clamp(0, 100)),
p {
background_color: "black",
+5 -3
View File
@@ -229,11 +229,13 @@ pub fn virtual_event_from_websys_event(event: web_sys::Event, target: Element) -
"change" | "input" | "invalid" | "reset" | "submit" => read_input_to_data(target),
"click" | "contextmenu" | "dblclick" | "doubleclick" | "drag" | "dragend" | "dragenter"
| "dragexit" | "dragleave" | "dragover" | "dragstart" | "drop" | "mousedown"
| "mouseenter" | "mouseleave" | "mousemove" | "mouseout" | "mouseover" | "mouseup" => {
"click" | "contextmenu" | "dblclick" | "doubleclick" | "mousedown" | "mouseenter"
| "mouseleave" | "mousemove" | "mouseout" | "mouseover" | "mouseup" => {
Rc::new(MouseData::from(event))
}
"drag" | "dragend" | "dragenter" | "dragexit" | "dragleave" | "dragover" | "dragstart"
| "drop" => Rc::new(DragData::from(event)),
"pointerdown" | "pointermove" | "pointerup" | "pointercancel" | "gotpointercapture"
| "lostpointercapture" | "pointerenter" | "pointerleave" | "pointerover" | "pointerout" => {
Rc::new(PointerData::from(event))