From a9b7da18902fcea0564a462a6b058a6087f51c22 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Fri, 9 Dec 2022 12:00:37 -0600 Subject: [PATCH 001/432] remove some unused files --- packages/rsx/src/attributes.rs | 892 -------------------------- packages/rsx/src/template.rs | 1064 -------------------------------- 2 files changed, 1956 deletions(-) delete mode 100644 packages/rsx/src/attributes.rs delete mode 100644 packages/rsx/src/template.rs diff --git a/packages/rsx/src/attributes.rs b/packages/rsx/src/attributes.rs deleted file mode 100644 index 8920d69af..000000000 --- a/packages/rsx/src/attributes.rs +++ /dev/null @@ -1,892 +0,0 @@ -use crate::elements::*; - -// map the rsx name of the attribute to the html name of the attribute, the namespace that contains it, and if the attribute is volitile -pub fn attrbute_to_static_str( - attr: &str, - element: &'static str, - namespace: Option<&'static str>, -) -> Option<(&'static str, Option<&'static str>, bool)> { - if namespace == Some("http://www.w3.org/2000/svg") { - svg::MAPPED_ATTRIBUTES - .iter() - .find(|(a, _)| *a == attr) - .map(|(_, b)| (*b, None, false)) - } else { - NO_NAMESPACE_ATTRIBUTES - .iter() - .find(|&a| *a == attr) - .map(|a| (*a, None, false)) - .or_else(|| { - STYLE_ATTRIBUTES - .iter() - .find(|(a, _)| *a == attr) - .map(|(_, b)| (*b, Some("style"), false)) - }) - .or_else(|| { - MAPPED_ATTRIBUTES - .iter() - .find(|(a, _)| *a == attr) - .map(|(_, b)| (*b, None, false)) - }) - } - .or_else(|| { - ELEMENTS_WITH_MAPPED_ATTRIBUTES - .iter() - .find_map(|(el, attrs)| { - (element == *el) - .then(|| { - attrs - .iter() - .find(|(a, _, _)| *a == attr) - .map(|(_, b, volitile)| (*b, None, *volitile)) - }) - .flatten() - }) - }) - .or_else(|| { - ELEMENTS_WITH_NAMESPACE.iter().find_map(|(el, ns, attrs)| { - (element == *el && namespace == Some(*ns)) - .then(|| { - attrs - .iter() - .find(|a| **a == attr) - .map(|a| (*a, None, false)) - }) - .flatten() - }) - }) - .or_else(|| { - ELEMENTS_WITHOUT_NAMESPACE.iter().find_map(|(el, attrs)| { - (element == *el) - .then(|| { - attrs - .iter() - .find(|a| **a == attr) - .map(|a| (*a, None, false)) - }) - .flatten() - }) - }) -} - -macro_rules! no_namespace_trait_methods { - ( - $( - $(#[$attr:meta])* - $name:ident; - )* - ) => { - pub const NO_NAMESPACE_ATTRIBUTES: &'static [&'static str] = &[ - $( - stringify!($name), - )* - ]; - }; -} - -macro_rules! style_trait_methods { - ( - $( - $(#[$attr:meta])* - $name:ident: $lit:literal, - )* - ) => { - pub const STYLE_ATTRIBUTES: &'static [(&'static str, &'static str)] = &[ - $( - (stringify!($name), $lit), - )* - ]; - }; -} - -macro_rules! mapped_trait_methods { - ( - $( - $(#[$attr:meta])* - $name:ident: $lit:literal, - )* - ) => { - pub const MAPPED_ATTRIBUTES: &'static [(&'static str, &'static str)] = &[ - $( - (stringify!($name), $lit), - )* - ("prevent_default", "dioxus-prevent-default"), - ]; - }; -} - -no_namespace_trait_methods! { - accesskey; - - /// The HTML class attribute is used to specify a class for an HTML element. - /// - /// ## Details - /// Multiple HTML elements can share the same class. - /// - /// The class global attribute is a space-separated list of the case-sensitive classes of the element. - /// Classes allow CSS and Javascript to select and access specific elements via the class selectors or - /// functions like the DOM method document.getElementsByClassName. - /// - /// ## Example - /// - /// ### HTML: - /// ```html - ///

Above point sounds a bit obvious. Remove/rewrite?

- /// ``` - /// - /// ### CSS: - /// ```css - /// .note { - /// font-style: italic; - /// font-weight: bold; - /// } - /// - /// .editorial { - /// background: rgb(255, 0, 0, .25); - /// padding: 10px; - /// } - /// ``` - class; - contenteditable; - data; - dir; - draggable; - hidden; - id; - lang; - spellcheck; - style; - tabindex; - title; - translate; - - role; - - /// dangerous_inner_html is Dioxus's replacement for using innerHTML in the browser DOM. In general, setting - /// HTML from code is risky because it’s easy to inadvertently expose your users to a cross-site scripting (XSS) - /// attack. So, you can set HTML directly from Dioxus, but you have to type out dangerous_inner_html to remind - /// yourself that it’s dangerous - dangerous_inner_html; -} - -// This macro creates an explicit method call for each of the style attributes. -// -// The left token specifies the name of the attribute in the rsx! macro, and the right string literal specifies the -// actual name of the attribute generated. -// -// This roughly follows the html spec -style_trait_methods! { - align_content: "align-content", - align_items: "align-items", - align_self: "align-self", - alignment_adjust: "alignment-adjust", - alignment_baseline: "alignment-baseline", - all: "all", - alt: "alt", - animation: "animation", - animation_delay: "animation-delay", - animation_direction: "animation-direction", - animation_duration: "animation-duration", - animation_fill_mode: "animation-fill-mode", - animation_iteration_count: "animation-iteration-count", - animation_name: "animation-name", - animation_play_state: "animation-play-state", - animation_timing_function: "animation-timing-function", - azimuth: "azimuth", - backface_visibility: "backface-visibility", - background: "background", - background_attachment: "background-attachment", - background_clip: "background-clip", - background_color: "background-color", - background_image: "background-image", - background_origin: "background-origin", - background_position: "background-position", - background_repeat: "background-repeat", - background_size: "background-size", - background_blend_mode: "background-blend-mode", - baseline_shift: "baseline-shift", - bleed: "bleed", - bookmark_label: "bookmark-label", - bookmark_level: "bookmark-level", - bookmark_state: "bookmark-state", - border: "border", - border_color: "border-color", - border_style: "border-style", - border_width: "border-width", - border_bottom: "border-bottom", - border_bottom_color: "border-bottom-color", - border_bottom_style: "border-bottom-style", - border_bottom_width: "border-bottom-width", - border_left: "border-left", - border_left_color: "border-left-color", - border_left_style: "border-left-style", - border_left_width: "border-left-width", - border_right: "border-right", - border_right_color: "border-right-color", - border_right_style: "border-right-style", - border_right_width: "border-right-width", - border_top: "border-top", - border_top_color: "border-top-color", - border_top_style: "border-top-style", - border_top_width: "border-top-width", - border_collapse: "border-collapse", - border_image: "border-image", - border_image_outset: "border-image-outset", - border_image_repeat: "border-image-repeat", - border_image_slice: "border-image-slice", - border_image_source: "border-image-source", - border_image_width: "border-image-width", - border_radius: "border-radius", - border_bottom_left_radius: "border-bottom-left-radius", - border_bottom_right_radius: "border-bottom-right-radius", - border_top_left_radius: "border-top-left-radius", - border_top_right_radius: "border-top-right-radius", - border_spacing: "border-spacing", - bottom: "bottom", - box_decoration_break: "box-decoration-break", - box_shadow: "box-shadow", - box_sizing: "box-sizing", - box_snap: "box-snap", - break_after: "break-after", - break_before: "break-before", - break_inside: "break-inside", - buffered_rendering: "buffered-rendering", - caption_side: "caption-side", - clear: "clear", - clear_side: "clear-side", - clip: "clip", - clip_path: "clip-path", - clip_rule: "clip-rule", - color: "color", - color_adjust: "color-adjust", - color_correction: "color-correction", - color_interpolation: "color-interpolation", - color_interpolation_filters: "color-interpolation-filters", - color_profile: "color-profile", - color_rendering: "color-rendering", - column_fill: "column-fill", - column_gap: "column-gap", - column_rule: "column-rule", - column_rule_color: "column-rule-color", - column_rule_style: "column-rule-style", - column_rule_width: "column-rule-width", - column_span: "column-span", - columns: "columns", - column_count: "column-count", - column_width: "column-width", - contain: "contain", - content: "content", - counter_increment: "counter-increment", - counter_reset: "counter-reset", - counter_set: "counter-set", - cue: "cue", - cue_after: "cue-after", - cue_before: "cue-before", - cursor: "cursor", - direction: "direction", - display: "display", - display_inside: "display-inside", - display_outside: "display-outside", - display_extras: "display-extras", - display_box: "display-box", - dominant_baseline: "dominant-baseline", - elevation: "elevation", - empty_cells: "empty-cells", - enable_background: "enable-background", - fill: "fill", - fill_opacity: "fill-opacity", - fill_rule: "fill-rule", - filter: "filter", - float: "float", - float_defer_column: "float-defer-column", - float_defer_page: "float-defer-page", - float_offset: "float-offset", - float_wrap: "float-wrap", - flow_into: "flow-into", - flow_from: "flow-from", - flex: "flex", - flex_basis: "flex-basis", - flex_grow: "flex-grow", - flex_shrink: "flex-shrink", - flex_flow: "flex-flow", - flex_direction: "flex-direction", - flex_wrap: "flex-wrap", - flood_color: "flood-color", - flood_opacity: "flood-opacity", - font: "font", - font_family: "font-family", - font_size: "font-size", - font_stretch: "font-stretch", - font_style: "font-style", - font_weight: "font-weight", - font_feature_settings: "font-feature-settings", - font_kerning: "font-kerning", - font_language_override: "font-language-override", - font_size_adjust: "font-size-adjust", - font_synthesis: "font-synthesis", - font_variant: "font-variant", - font_variant_alternates: "font-variant-alternates", - font_variant_caps: "font-variant-caps", - font_variant_east_asian: "font-variant-east-asian", - font_variant_ligatures: "font-variant-ligatures", - font_variant_numeric: "font-variant-numeric", - font_variant_position: "font-variant-position", - footnote_policy: "footnote-policy", - glyph_orientation_horizontal: "glyph-orientation-horizontal", - glyph_orientation_vertical: "glyph-orientation-vertical", - grid: "grid", - grid_auto_flow: "grid-auto-flow", - grid_auto_columns: "grid-auto-columns", - grid_auto_rows: "grid-auto-rows", - grid_template: "grid-template", - grid_template_areas: "grid-template-areas", - grid_template_columns: "grid-template-columns", - grid_template_rows: "grid-template-rows", - grid_area: "grid-area", - grid_column: "grid-column", - grid_column_start: "grid-column-start", - grid_column_end: "grid-column-end", - grid_row: "grid-row", - grid_row_start: "grid-row-start", - grid_row_end: "grid-row-end", - hanging_punctuation: "hanging-punctuation", - height: "height", - hyphenate_character: "hyphenate-character", - hyphenate_limit_chars: "hyphenate-limit-chars", - hyphenate_limit_last: "hyphenate-limit-last", - hyphenate_limit_lines: "hyphenate-limit-lines", - hyphenate_limit_zone: "hyphenate-limit-zone", - hyphens: "hyphens", - icon: "icon", - image_orientation: "image-orientation", - image_resolution: "image-resolution", - image_rendering: "image-rendering", - ime: "ime", - ime_align: "ime-align", - ime_mode: "ime-mode", - ime_offset: "ime-offset", - ime_width: "ime-width", - initial_letters: "initial-letters", - inline_box_align: "inline-box-align", - isolation: "isolation", - justify_content: "justify-content", - justify_items: "justify-items", - justify_self: "justify-self", - kerning: "kerning", - left: "left", - letter_spacing: "letter-spacing", - lighting_color: "lighting-color", - line_box_contain: "line-box-contain", - line_break: "line-break", - line_grid: "line-grid", - line_height: "line-height", - line_slack: "line-slack", - line_snap: "line-snap", - list_style: "list-style", - list_style_image: "list-style-image", - list_style_position: "list-style-position", - list_style_type: "list-style-type", - margin: "margin", - margin_bottom: "margin-bottom", - margin_left: "margin-left", - margin_right: "margin-right", - margin_top: "margin-top", - marker: "marker", - marker_end: "marker-end", - marker_mid: "marker-mid", - marker_pattern: "marker-pattern", - marker_segment: "marker-segment", - marker_start: "marker-start", - marker_knockout_left: "marker-knockout-left", - marker_knockout_right: "marker-knockout-right", - marker_side: "marker-side", - marks: "marks", - marquee_direction: "marquee-direction", - marquee_play_count: "marquee-play-count", - marquee_speed: "marquee-speed", - marquee_style: "marquee-style", - mask: "mask", - mask_image: "mask-image", - mask_repeat: "mask-repeat", - mask_position: "mask-position", - mask_clip: "mask-clip", - mask_origin: "mask-origin", - mask_size: "mask-size", - mask_box: "mask-box", - mask_box_outset: "mask-box-outset", - mask_box_repeat: "mask-box-repeat", - mask_box_slice: "mask-box-slice", - mask_box_source: "mask-box-source", - mask_box_width: "mask-box-width", - mask_type: "mask-type", - max_height: "max-height", - max_lines: "max-lines", - max_width: "max-width", - min_height: "min-height", - min_width: "min-width", - mix_blend_mode: "mix-blend-mode", - nav_down: "nav-down", - nav_index: "nav-index", - nav_left: "nav-left", - nav_right: "nav-right", - nav_up: "nav-up", - object_fit: "object-fit", - object_position: "object-position", - offset_after: "offset-after", - offset_before: "offset-before", - offset_end: "offset-end", - offset_start: "offset-start", - opacity: "opacity", - order: "order", - orphans: "orphans", - outline: "outline", - outline_color: "outline-color", - outline_style: "outline-style", - outline_width: "outline-width", - outline_offset: "outline-offset", - overflow: "overflow", - overflow_x: "overflow-x", - overflow_y: "overflow-y", - overflow_style: "overflow-style", - overflow_wrap: "overflow-wrap", - padding: "padding", - padding_bottom: "padding-bottom", - padding_left: "padding-left", - padding_right: "padding-right", - padding_top: "padding-top", - page: "page", - page_break_after: "page-break-after", - page_break_before: "page-break-before", - page_break_inside: "page-break-inside", - paint_order: "paint-order", - pause: "pause", - pause_after: "pause-after", - pause_before: "pause-before", - perspective: "perspective", - perspective_origin: "perspective-origin", - pitch: "pitch", - pitch_range: "pitch-range", - play_during: "play-during", - pointer_events: "pointer-events", - position: "position", - quotes: "quotes", - region_fragment: "region-fragment", - resize: "resize", - rest: "rest", - rest_after: "rest-after", - rest_before: "rest-before", - richness: "richness", - right: "right", - ruby_align: "ruby-align", - ruby_merge: "ruby-merge", - ruby_position: "ruby-position", - scroll_behavior: "scroll-behavior", - scroll_snap_coordinate: "scroll-snap-coordinate", - scroll_snap_destination: "scroll-snap-destination", - scroll_snap_points_x: "scroll-snap-points-x", - scroll_snap_points_y: "scroll-snap-points-y", - scroll_snap_type: "scroll-snap-type", - shape_image_threshold: "shape-image-threshold", - shape_inside: "shape-inside", - shape_margin: "shape-margin", - shape_outside: "shape-outside", - shape_padding: "shape-padding", - shape_rendering: "shape-rendering", - size: "size", - speak: "speak", - speak_as: "speak-as", - speak_header: "speak-header", - speak_numeral: "speak-numeral", - speak_punctuation: "speak-punctuation", - speech_rate: "speech-rate", - stop_color: "stop-color", - stop_opacity: "stop-opacity", - stress: "stress", - string_set: "string-set", - stroke: "stroke", - stroke_dasharray: "stroke-dasharray", - stroke_dashoffset: "stroke-dashoffset", - stroke_linecap: "stroke-linecap", - stroke_linejoin: "stroke-linejoin", - stroke_miterlimit: "stroke-miterlimit", - stroke_opacity: "stroke-opacity", - stroke_width: "stroke-width", - tab_size: "tab-size", - table_layout: "table-layout", - text_align: "text-align", - text_align_all: "text-align-all", - text_align_last: "text-align-last", - text_anchor: "text-anchor", - text_combine_upright: "text-combine-upright", - text_decoration: "text-decoration", - text_decoration_color: "text-decoration-color", - text_decoration_line: "text-decoration-line", - text_decoration_style: "text-decoration-style", - text_decoration_skip: "text-decoration-skip", - text_emphasis: "text-emphasis", - text_emphasis_color: "text-emphasis-color", - text_emphasis_style: "text-emphasis-style", - text_emphasis_position: "text-emphasis-position", - text_emphasis_skip: "text-emphasis-skip", - text_height: "text-height", - text_indent: "text-indent", - text_justify: "text-justify", - text_orientation: "text-orientation", - text_overflow: "text-overflow", - text_rendering: "text-rendering", - text_shadow: "text-shadow", - text_size_adjust: "text-size-adjust", - text_space_collapse: "text-space-collapse", - text_spacing: "text-spacing", - text_transform: "text-transform", - text_underline_position: "text-underline-position", - text_wrap: "text-wrap", - top: "top", - touch_action: "touch-action", - transform: "transform", - transform_box: "transform-box", - transform_origin: "transform-origin", - transform_style: "transform-style", - transition: "transition", - transition_delay: "transition-delay", - transition_duration: "transition-duration", - transition_property: "transition-property", - unicode_bidi: "unicode-bidi", - vector_effect: "vector-effect", - vertical_align: "vertical-align", - visibility: "visibility", - voice_balance: "voice-balance", - voice_duration: "voice-duration", - voice_family: "voice-family", - voice_pitch: "voice-pitch", - voice_range: "voice-range", - voice_rate: "voice-rate", - voice_stress: "voice-stress", - voice_volumn: "voice-volumn", - volume: "volume", - white_space: "white-space", - widows: "widows", - width: "width", - will_change: "will-change", - word_break: "word-break", - word_spacing: "word-spacing", - word_wrap: "word-wrap", - wrap_flow: "wrap-flow", - wrap_through: "wrap-through", - writing_mode: "writing-mode", - gap: "gap", - list_styler_type: "list-style-type", - row_gap: "row-gap", - transition_timing_function: "transition-timing-function", - user_select: "user-select", - webkit_user_select: "-webkit-user-select", - z_index : "z-index", -} -mapped_trait_methods! { - aria_current: "aria-current", - aria_details: "aria-details", - aria_disabled: "aria-disabled", - aria_hidden: "aria-hidden", - aria_invalid: "aria-invalid", - aria_keyshortcuts: "aria-keyshortcuts", - aria_label: "aria-label", - aria_roledescription: "aria-roledescription", - // Widget Attributes - aria_autocomplete: "aria-autocomplete", - aria_checked: "aria-checked", - aria_expanded: "aria-expanded", - aria_haspopup: "aria-haspopup", - aria_level: "aria-level", - aria_modal: "aria-modal", - aria_multiline: "aria-multiline", - aria_multiselectable: "aria-multiselectable", - aria_orientation: "aria-orientation", - aria_placeholder: "aria-placeholder", - aria_pressed: "aria-pressed", - aria_readonly: "aria-readonly", - aria_required: "aria-required", - aria_selected: "aria-selected", - aria_sort: "aria-sort", - aria_valuemax: "aria-valuemax", - aria_valuemin: "aria-valuemin", - aria_valuenow: "aria-valuenow", - aria_valuetext: "aria-valuetext", - // Live Region Attributes - aria_atomic: "aria-atomic", - aria_busy: "aria-busy", - aria_live: "aria-live", - aria_relevant: "aria-relevant", - - aria_dropeffect: "aria-dropeffect", - aria_grabbed: "aria-grabbed", - // Relationship Attributes - aria_activedescendant: "aria-activedescendant", - aria_colcount: "aria-colcount", - aria_colindex: "aria-colindex", - aria_colspan: "aria-colspan", - aria_controls: "aria-controls", - aria_describedby: "aria-describedby", - aria_errormessage: "aria-errormessage", - aria_flowto: "aria-flowto", - aria_labelledby: "aria-labelledby", - aria_owns: "aria-owns", - aria_posinset: "aria-posinset", - aria_rowcount: "aria-rowcount", - aria_rowindex: "aria-rowindex", - aria_rowspan: "aria-rowspan", - aria_setsize: "aria-setsize", -} - -pub mod svg { - mapped_trait_methods! { - accent_height: "accent-height", - accumulate: "accumulate", - additive: "additive", - alignment_baseline: "alignment-baseline", - alphabetic: "alphabetic", - amplitude: "amplitude", - arabic_form: "arabic-form", - ascent: "ascent", - attributeName: "attributeName", - attributeType: "attributeType", - azimuth: "azimuth", - baseFrequency: "baseFrequency", - baseline_shift: "baseline-shift", - baseProfile: "baseProfile", - bbox: "bbox", - begin: "begin", - bias: "bias", - by: "by", - calcMode: "calcMode", - cap_height: "cap-height", - class: "class", - clip: "clip", - clipPathUnits: "clipPathUnits", - clip_path: "clip-path", - clip_rule: "clip-rule", - color: "color", - color_interpolation: "color-interpolation", - color_interpolation_filters: "color-interpolation-filters", - color_profile: "color-profile", - color_rendering: "color-rendering", - contentScriptType: "contentScriptType", - contentStyleType: "contentStyleType", - crossorigin: "crossorigin", - cursor: "cursor", - cx: "cx", - cy: "cy", - d: "d", - decelerate: "decelerate", - descent: "descent", - diffuseConstant: "diffuseConstant", - direction: "direction", - display: "display", - divisor: "divisor", - dominant_baseline: "dominant-baseline", - dur: "dur", - dx: "dx", - dy: "dy", - edgeMode: "edgeMode", - elevation: "elevation", - enable_background: "enable-background", - end: "end", - exponent: "exponent", - fill: "fill", - fill_opacity: "fill-opacity", - fill_rule: "fill-rule", - filter: "filter", - filterRes: "filterRes", - filterUnits: "filterUnits", - flood_color: "flood-color", - flood_opacity: "flood-opacity", - font_family: "font-family", - font_size: "font-size", - font_size_adjust: "font-size-adjust", - font_stretch: "font-stretch", - font_style: "font-style", - font_variant: "font-variant", - font_weight: "font-weight", - format: "format", - from: "from", - fr: "fr", - fx: "fx", - fy: "fy", - g1: "g1", - g2: "g2", - glyph_name: "glyph-name", - glyph_orientation_horizontal: "glyph-orientation-horizontal", - glyph_orientation_vertical: "glyph-orientation-vertical", - glyphRef: "glyphRef", - gradientTransform: "gradientTransform", - gradientUnits: "gradientUnits", - hanging: "hanging", - height: "height", - href: "href", - hreflang: "hreflang", - horiz_adv_x: "horiz-adv-x", - horiz_origin_x: "horiz-origin-x", - id: "id", - ideographic: "ideographic", - image_rendering: "image-rendering", - _in: "_in", - in2: "in2", - intercept: "intercept", - k: "k", - k1: "k1", - k2: "k2", - k3: "k3", - k4: "k4", - kernelMatrix: "kernelMatrix", - kernelUnitLength: "kernelUnitLength", - kerning: "kerning", - keyPoints: "keyPoints", - keySplines: "keySplines", - keyTimes: "keyTimes", - lang: "lang", - lengthAdjust: "lengthAdjust", - letter_spacing: "letter-spacing", - lighting_color: "lighting-color", - limitingConeAngle: "limitingConeAngle", - local: "local", - marker_end: "marker-end", - marker_mid: "marker-mid", - marker_start: "marker_start", - markerHeight: "markerHeight", - markerUnits: "markerUnits", - markerWidth: "markerWidth", - mask: "mask", - maskContentUnits: "maskContentUnits", - maskUnits: "maskUnits", - mathematical: "mathematical", - max: "max", - media: "media", - method: "method", - min: "min", - mode: "mode", - name: "name", - numOctaves: "numOctaves", - offset: "offset", - opacity: "opacity", - operator: "operator", - order: "order", - orient: "orient", - orientation: "orientation", - origin: "origin", - overflow: "overflow", - overline_position: "overline-position", - overline_thickness: "overline-thickness", - panose_1: "panose-1", - paint_order: "paint-order", - path: "path", - pathLength: "pathLength", - patternContentUnits: "patternContentUnits", - patternTransform: "patternTransform", - patternUnits: "patternUnits", - ping: "ping", - pointer_events: "pointer-events", - points: "points", - pointsAtX: "pointsAtX", - pointsAtY: "pointsAtY", - pointsAtZ: "pointsAtZ", - preserveAlpha: "preserveAlpha", - preserveAspectRatio: "preserveAspectRatio", - primitiveUnits: "primitiveUnits", - r: "r", - radius: "radius", - referrerPolicy: "referrerPolicy", - refX: "refX", - refY: "refY", - rel: "rel", - rendering_intent: "rendering-intent", - repeatCount: "repeatCount", - repeatDur: "repeatDur", - requiredExtensions: "requiredExtensions", - requiredFeatures: "requiredFeatures", - restart: "restart", - result: "result", - role: "role", - rotate: "rotate", - rx: "rx", - ry: "ry", - scale: "scale", - seed: "seed", - shape_rendering: "shape-rendering", - slope: "slope", - spacing: "spacing", - specularConstant: "specularConstant", - specularExponent: "specularExponent", - speed: "speed", - spreadMethod: "spreadMethod", - startOffset: "startOffset", - stdDeviation: "stdDeviation", - stemh: "stemh", - stemv: "stemv", - stitchTiles: "stitchTiles", - stop_color: "stop_color", - stop_opacity: "stop_opacity", - strikethrough_position: "strikethrough-position", - strikethrough_thickness: "strikethrough-thickness", - string: "string", - stroke: "stroke", - stroke_dasharray: "stroke-dasharray", - stroke_dashoffset: "stroke-dashoffset", - stroke_linecap: "stroke-linecap", - stroke_linejoin: "stroke-linejoin", - stroke_miterlimit: "stroke-miterlimit", - stroke_opacity: "stroke-opacity", - stroke_width: "stroke-width", - style: "style", - surfaceScale: "surfaceScale", - systemLanguage: "systemLanguage", - tabindex: "tabindex", - tableValues: "tableValues", - target: "target", - targetX: "targetX", - targetY: "targetY", - text_anchor: "text-anchor", - text_decoration: "text-decoration", - text_rendering: "text-rendering", - textLength: "textLength", - to: "to", - transform: "transform", - transform_origin: "transform-origin", - r#type: "_type", - u1: "u1", - u2: "u2", - underline_position: "underline-position", - underline_thickness: "underline-thickness", - unicode: "unicode", - unicode_bidi: "unicode-bidi", - unicode_range: "unicode-range", - units_per_em: "units-per-em", - v_alphabetic: "v-alphabetic", - v_hanging: "v-hanging", - v_ideographic: "v-ideographic", - v_mathematical: "v-mathematical", - values: "values", - vector_effect: "vector-effect", - version: "version", - vert_adv_y: "vert-adv-y", - vert_origin_x: "vert-origin-x", - vert_origin_y: "vert-origin-y", - view_box: "viewBox", - view_target: "viewTarget", - visibility: "visibility", - width: "width", - widths: "widths", - word_spacing: "word-spacing", - writing_mode: "writing-mode", - x: "x", - x_height: "x-height", - x1: "x1", - x2: "x2", - xmlns: "xmlns", - x_channel_selector: "xChannelSelector", - y: "y", - y1: "y1", - y2: "y2", - y_channel_selector: "yChannelSelector", - z: "z", - zoomAndPan: "zoomAndPan", - } -} diff --git a/packages/rsx/src/template.rs b/packages/rsx/src/template.rs deleted file mode 100644 index 22c73ae33..000000000 --- a/packages/rsx/src/template.rs +++ /dev/null @@ -1,1064 +0,0 @@ -// use proc_macro2::TokenStream; -// use quote::TokenStreamExt; -// use quote::{quote, ToTokens}; -// use syn::{Expr, Ident, LitStr}; - -// #[cfg(any(feature = "hot-reload", debug_assertions))] -// pub fn try_parse_template( -// rsx: &str, -// location: OwnedCodeLocation, -// previous_template: Option, -// ) -> Result<(OwnedTemplate, DynamicTemplateContextBuilder), Error> { -// use crate::CallBody; - -// let call_body: CallBody = -// syn::parse_str(rsx).map_err(|e| Error::ParseError(ParseError::new(e, location.clone())))?; -// let mut template_builder = TemplateBuilder::from_roots_always(call_body.roots); -// if let Some(prev) = previous_template { -// template_builder = template_builder -// .try_switch_dynamic_context(prev) -// .ok_or_else(|| { -// Error::RecompileRequiredError(RecompileReason::Variable( -// "dynamic context updated".to_string(), -// )) -// })?; -// } -// let dyn_ctx = template_builder.dynamic_context.clone(); -// Ok((template_builder.try_into_owned(&location)?, dyn_ctx)) -// } - -// use crate::{BodyNode, ElementAttr, FormattedSegment, Segment}; - -// struct TemplateElementBuilder { -// tag: Ident, -// attributes: Vec, -// children: Vec, -// listeners: Vec, -// locally_static: bool, -// } - -// #[cfg(any(feature = "hot-reload", debug_assertions))] -// type OwndedTemplateElement = TemplateElement< -// Vec>, -// OwnedAttributeValue, -// Vec, -// Vec, -// >; - -// impl TemplateElementBuilder { -// #[cfg(any(feature = "hot-reload", debug_assertions))] -// fn try_into_owned(self, location: &OwnedCodeLocation) -> Result { -// let Self { -// tag, -// attributes, -// children, -// listeners, -// .. -// } = self; -// let (element_tag, element_ns) = -// element_to_static_str(&tag.to_string()).ok_or_else(|| { -// Error::ParseError(ParseError::new( -// syn::Error::new(tag.span(), format!("unknown element: {}", tag)), -// location.clone(), -// )) -// })?; - -// let mut owned_attributes = Vec::new(); -// for a in attributes { -// owned_attributes.push(a.try_into_owned(location, element_tag, element_ns)?); -// } - -// Ok(TemplateElement::new( -// element_tag, -// element_ns, -// owned_attributes, -// children, -// listeners, -// )) -// } -// } - -// impl ToTokens for TemplateElementBuilder { -// fn to_tokens(&self, tokens: &mut TokenStream) { -// let Self { -// tag, -// attributes, -// children, -// listeners, -// .. -// } = self; -// let children = children.iter().map(|id| { -// let raw = id.0; -// quote! {TemplateNodeId(#raw)} -// }); -// tokens.append_all(quote! { -// TemplateElement::new( -// dioxus_elements::#tag::TAG_NAME, -// dioxus_elements::#tag::NAME_SPACE, -// &[#(#attributes),*], -// &[#(#children),*], -// &[#(#listeners),*], -// ) -// }) -// } -// } - -// enum AttributeName { -// Ident(Ident), -// Str(LitStr), -// } - -// struct TemplateAttributeBuilder { -// element_tag: Ident, -// name: AttributeName, -// value: TemplateAttributeValue, -// } - -// impl TemplateAttributeBuilder { -// #[cfg(any(feature = "hot-reload", debug_assertions))] -// fn try_into_owned( -// self, -// location: &OwnedCodeLocation, -// element_tag: &'static str, -// element_ns: Option<&'static str>, -// ) -> Result, Error> { -// let Self { name, value, .. } = self; -// let (name, span, literal) = match name { -// AttributeName::Ident(name) => (name.to_string(), name.span(), false), -// AttributeName::Str(name) => (name.value(), name.span(), true), -// }; -// let (name, namespace, volatile) = attrbute_to_static_str(&name, element_tag, element_ns) -// .ok_or_else(|| { -// if literal { -// // literals will be captured when a full recompile is triggered -// Error::RecompileRequiredError(RecompileReason::Attribute(name.to_string())) -// } else { -// Error::ParseError(ParseError::new( -// syn::Error::new(span, format!("unknown attribute: {}", name)), -// location.clone(), -// )) -// } -// })?; -// let attribute = AttributeDiscription { -// name, -// namespace, -// volatile, -// }; -// Ok(TemplateAttribute { value, attribute }) -// } -// } - -// impl ToTokens for TemplateAttributeBuilder { -// fn to_tokens(&self, tokens: &mut TokenStream) { -// let Self { -// element_tag, -// name, -// value, -// } = self; -// let value = match value { -// TemplateAttributeValue::Static(val) => { -// let val = match val { -// OwnedAttributeValue::Text(txt) => quote! {StaticAttributeValue::Text(#txt)}, -// OwnedAttributeValue::Float32(f) => quote! {StaticAttributeValue::Float32(#f)}, -// OwnedAttributeValue::Float64(f) => quote! {StaticAttributeValue::Float64(#f)}, -// OwnedAttributeValue::Int32(i) => quote! {StaticAttributeValue::Int32(#i)}, -// OwnedAttributeValue::Int64(i) => quote! {StaticAttributeValue::Int64(#i)}, -// OwnedAttributeValue::Uint32(u) => quote! {StaticAttributeValue::Uint32(#u)}, -// OwnedAttributeValue::Uint64(u) => quote! {StaticAttributeValue::Uint64(#u)}, -// OwnedAttributeValue::Bool(b) => quote! {StaticAttributeValue::Bool(#b)}, -// OwnedAttributeValue::Vec3Float(f1, f2, f3) => { -// quote! {StaticAttributeValue::Vec3Float(#f1, #f2, #f3)} -// } -// OwnedAttributeValue::Vec3Int(f1, f2, f3) => { -// quote! {StaticAttributeValue::Vec3Int(#f1, #f2, #f3)} -// } -// OwnedAttributeValue::Vec3Uint(f1, f2, f3) => { -// quote! {StaticAttributeValue::Vec3Uint(#f1, #f2, #f3)} -// } -// OwnedAttributeValue::Vec4Float(f1, f2, f3, f4) => { -// quote! {StaticAttributeValue::Vec4Float(#f1, #f2, #f3, #f4)} -// } -// OwnedAttributeValue::Vec4Int(f1, f2, f3, f4) => { -// quote! {StaticAttributeValue::Vec4Int(#f1, #f2, #f3, #f4)} -// } -// OwnedAttributeValue::Vec4Uint(f1, f2, f3, f4) => { -// quote! {StaticAttributeValue::Vec4Uint(#f1, #f2, #f3, #f4)} -// } -// OwnedAttributeValue::Bytes(b) => { -// quote! {StaticAttributeValue::Bytes(&[#(#b),*])} -// } -// OwnedAttributeValue::Any(_) => todo!(), -// }; -// quote! {TemplateAttributeValue::Static(#val)} -// } -// TemplateAttributeValue::Dynamic(idx) => quote! {TemplateAttributeValue::Dynamic(#idx)}, -// }; -// match name { -// AttributeName::Ident(name) => tokens.append_all(quote! { -// TemplateAttribute{ -// attribute: dioxus_elements::#element_tag::#name, -// value: #value, -// } -// }), -// AttributeName::Str(lit) => tokens.append_all(quote! { -// TemplateAttribute{ -// attribute: dioxus::prelude::AttributeDiscription{ -// name: #lit, -// namespace: None, -// volatile: false -// }, -// value: #value, -// } -// }), -// } -// } -// } - -// enum TemplateNodeTypeBuilder { -// Element(TemplateElementBuilder), -// Text(TextTemplate>, String>), -// DynamicNode(usize), -// } - -// #[cfg(any(feature = "hot-reload", debug_assertions))] -// type OwnedTemplateNodeType = TemplateNodeType< -// Vec>, -// OwnedAttributeValue, -// Vec, -// Vec, -// Vec>, -// String, -// >; - -// impl TemplateNodeTypeBuilder { -// #[cfg(any(feature = "hot-reload", debug_assertions))] -// fn try_into_owned(self, location: &OwnedCodeLocation) -> Result { -// match self { -// TemplateNodeTypeBuilder::Element(el) => { -// Ok(TemplateNodeType::Element(el.try_into_owned(location)?)) -// } -// TemplateNodeTypeBuilder::Text(txt) => Ok(TemplateNodeType::Text(txt)), -// TemplateNodeTypeBuilder::DynamicNode(idx) => Ok(TemplateNodeType::DynamicNode(idx)), -// } -// } -// } - -// impl ToTokens for TemplateNodeTypeBuilder { -// fn to_tokens(&self, tokens: &mut TokenStream) { -// match self { -// TemplateNodeTypeBuilder::Element(el) => tokens.append_all(quote! { -// TemplateNodeType::Element(#el) -// }), -// TemplateNodeTypeBuilder::Text(txt) => { -// let mut length = 0; - -// let segments = txt.segments.iter().map(|seg| match seg { -// TextTemplateSegment::Static(s) => { -// length += s.len(); -// quote!(TextTemplateSegment::Static(#s)) -// } -// TextTemplateSegment::Dynamic(idx) => quote!(TextTemplateSegment::Dynamic(#idx)), -// }); - -// tokens.append_all(quote! { -// TemplateNodeType::Text(TextTemplate::new(&[#(#segments),*], #length)) -// }); -// } -// TemplateNodeTypeBuilder::DynamicNode(idx) => tokens.append_all(quote! { -// TemplateNodeType::DynamicNode(#idx) -// }), -// } -// } -// } - -// struct TemplateNodeBuilder { -// id: TemplateNodeId, -// depth: usize, -// parent: Option, -// node_type: TemplateNodeTypeBuilder, -// fully_static: bool, -// } - -// impl TemplateNodeBuilder { -// #[cfg(any(feature = "hot-reload", debug_assertions))] -// fn try_into_owned(self, location: &OwnedCodeLocation) -> Result { -// let TemplateNodeBuilder { -// id, -// node_type, -// parent, -// depth, -// .. -// } = self; -// let node_type = node_type.try_into_owned(location)?; -// Ok(OwnedTemplateNode { -// id, -// node_type, -// parent, -// depth, -// }) -// } - -// fn to_tokens(&self, tokens: &mut TokenStream) { -// let Self { -// id, -// node_type, -// parent, -// depth, -// .. -// } = self; -// let raw_id = id.0; -// let parent = match parent { -// Some(id) => { -// let id = id.0; -// quote! {Some(TemplateNodeId(#id))} -// } -// None => quote! {None}, -// }; - -// tokens.append_all(quote! { -// TemplateNode { -// id: TemplateNodeId(#raw_id), -// node_type: #node_type, -// parent: #parent, -// depth: #depth, -// } -// }) -// } -// } - -// #[derive(Default)] -// pub struct TemplateBuilder { -// nodes: Vec, -// root_nodes: Vec, -// dynamic_context: DynamicTemplateContextBuilder, -// } - -// impl TemplateBuilder { -// /// Create a template builder from nodes if it would improve performance to do so. -// pub fn from_roots(roots: Vec) -> Option { -// let mut builder = Self::default(); - -// for (i, root) in roots.into_iter().enumerate() { -// let id = builder.build_node(root, None, vec![i], 0); -// builder.root_nodes.push(id); -// } - -// // only build a template if there is at least one static node -// if builder -// .nodes -// .iter() -// .all(|r| matches!(&r.node_type, TemplateNodeTypeBuilder::DynamicNode(_))) -// { -// None -// } else { -// Some(builder) -// } -// } - -// /// Create a template builder from nodes regardless of performance. -// #[cfg(any(feature = "hot-reload", debug_assertions))] -// fn from_roots_always(roots: Vec) -> Self { -// let mut builder = Self::default(); - -// for (i, root) in roots.into_iter().enumerate() { -// let id = builder.build_node(root, None, vec![i], 0); -// builder.root_nodes.push(id); -// } - -// builder -// } - -// fn build_node( -// &mut self, -// node: BodyNode, -// parent: Option, -// path: Vec, -// depth: usize, -// ) -> TemplateNodeId { -// let id = TemplateNodeId(self.nodes.len()); -// match node { -// BodyNode::Element(el) => { -// let mut locally_static = true; -// let mut attributes = Vec::new(); -// let mut listeners = Vec::new(); -// for attr in el.attributes { -// match attr.attr { -// ElementAttr::AttrText { name, value } => { -// if let Some(static_value) = value.to_static() { -// attributes.push(TemplateAttributeBuilder { -// element_tag: el.name.clone(), -// name: AttributeName::Ident(name), -// value: TemplateAttributeValue::Static( -// OwnedAttributeValue::Text(static_value), -// ), -// }) -// } else { -// locally_static = false; -// attributes.push(TemplateAttributeBuilder { -// element_tag: el.name.clone(), -// name: AttributeName::Ident(name), -// value: TemplateAttributeValue::Dynamic( -// self.dynamic_context.add_attr(quote!(#value)), -// ), -// }) -// } -// } -// ElementAttr::CustomAttrText { name, value } => { -// if let Some(static_value) = value.to_static() { -// attributes.push(TemplateAttributeBuilder { -// element_tag: el.name.clone(), -// name: AttributeName::Str(name), -// value: TemplateAttributeValue::Static( -// OwnedAttributeValue::Text(static_value), -// ), -// }) -// } else { -// locally_static = false; -// attributes.push(TemplateAttributeBuilder { -// element_tag: el.name.clone(), -// name: AttributeName::Str(name), -// value: TemplateAttributeValue::Dynamic( -// self.dynamic_context.add_attr(quote!(#value)), -// ), -// }) -// } -// } -// ElementAttr::AttrExpression { name, value } => { -// locally_static = false; -// attributes.push(TemplateAttributeBuilder { -// element_tag: el.name.clone(), -// name: AttributeName::Ident(name), -// value: TemplateAttributeValue::Dynamic( -// self.dynamic_context.add_attr(quote!(#value)), -// ), -// }) -// } -// ElementAttr::CustomAttrExpression { name, value } => { -// locally_static = false; -// attributes.push(TemplateAttributeBuilder { -// element_tag: el.name.clone(), -// name: AttributeName::Str(name), -// value: TemplateAttributeValue::Dynamic( -// self.dynamic_context.add_attr(quote!(#value)), -// ), -// }) -// } -// ElementAttr::EventTokens { name, tokens } => { -// locally_static = false; -// listeners.push(self.dynamic_context.add_listener(name, tokens)) -// } -// } -// } -// if let Some(key) = el.key { -// self.dynamic_context.add_key(quote!( -// dioxus::core::exports::bumpalo::format!(in __bump, "{}", #key) -// .into_bump_str() -// )); -// } -// self.nodes.push(TemplateNodeBuilder { -// id, -// node_type: TemplateNodeTypeBuilder::Element(TemplateElementBuilder { -// tag: el.name, -// attributes, -// children: Vec::new(), -// listeners, -// locally_static, -// }), -// parent, -// depth, -// fully_static: false, -// }); -// let children: Vec<_> = el -// .children -// .into_iter() -// .enumerate() -// .map(|(i, child)| { -// let mut new_path = path.clone(); -// new_path.push(i); -// self.build_node(child, Some(id), new_path, depth + 1) -// }) -// .collect(); -// let children_fully_static = children.iter().all(|c| self.nodes[c.0].fully_static); -// let parent = &mut self.nodes[id.0]; -// parent.fully_static = locally_static && children_fully_static; -// if let TemplateNodeTypeBuilder::Element(element) = &mut parent.node_type { -// element.children = children; -// } -// } - -// BodyNode::Component(comp) => { -// self.nodes.push(TemplateNodeBuilder { -// id, -// node_type: TemplateNodeTypeBuilder::DynamicNode( -// self.dynamic_context.add_node(BodyNode::Component(comp)), -// ), -// parent, -// depth, -// fully_static: false, -// }); -// } - -// BodyNode::Text(txt) => { -// let mut segments = Vec::new(); -// let mut length = 0; -// let mut fully_static = true; - -// for segment in txt.segments { -// segments.push(match segment { -// Segment::Literal(lit) => { -// length += lit.len(); -// TextTemplateSegment::Static(lit) -// } -// Segment::Formatted(fmted) => { -// fully_static = false; -// TextTemplateSegment::Dynamic(self.dynamic_context.add_text(fmted)) -// } -// }) -// } - -// self.nodes.push(TemplateNodeBuilder { -// id, -// node_type: TemplateNodeTypeBuilder::Text(TextTemplate::new(segments, length)), -// parent, -// depth, -// fully_static, -// }); -// } - -// BodyNode::RawExpr(expr) => { -// self.nodes.push(TemplateNodeBuilder { -// id, -// node_type: TemplateNodeTypeBuilder::DynamicNode( -// self.dynamic_context.add_node(BodyNode::RawExpr(expr)), -// ), -// parent, -// depth, -// fully_static: false, -// }); -// } -// } -// id -// } - -// #[cfg(any(feature = "hot-reload", debug_assertions))] -// pub fn try_switch_dynamic_context( -// mut self, -// dynamic_context: DynamicTemplateContextBuilder, -// ) -> Option { -// let attribute_mapping: HashMap = dynamic_context -// .attributes -// .iter() -// .enumerate() -// .map(|(i, ts)| (ts.to_string(), i)) -// .collect(); -// let text_mapping: HashMap = dynamic_context -// .text -// .iter() -// .enumerate() -// .map(|(i, ts)| (ts.to_token_stream().to_string(), i)) -// .collect(); -// let listener_mapping: HashMap<(String, Expr), usize> = dynamic_context -// .listeners -// .iter() -// .enumerate() -// .map(|(i, ts)| (ts.clone(), i)) -// .collect(); -// let node_mapping: HashMap = dynamic_context -// .nodes -// .iter() -// .enumerate() -// .map(|(i, ts)| (ts.to_token_stream().to_string(), i)) -// .collect(); - -// for node in &mut self.nodes { -// match &mut node.node_type { -// TemplateNodeTypeBuilder::Element(element) => { -// for listener in &mut element.listeners { -// *listener = -// *listener_mapping.get(&self.dynamic_context.listeners[*listener])?; -// } -// for attribute in &mut element.attributes { -// if let TemplateAttributeValue::Dynamic(idx) = &mut attribute.value { -// *idx = *attribute_mapping -// .get(&self.dynamic_context.attributes[*idx].to_string())?; -// } -// } -// } -// TemplateNodeTypeBuilder::Text(txt) => { -// for seg in &mut txt.segments { -// if let TextTemplateSegment::Dynamic(idx) = seg { -// *idx = *text_mapping.get( -// &self.dynamic_context.text[*idx] -// .to_token_stream() -// .to_string(), -// )?; -// } -// } -// } -// TemplateNodeTypeBuilder::DynamicNode(idx) => { -// *idx = *node_mapping.get( -// &self.dynamic_context.nodes[*idx] -// .to_token_stream() -// .to_string(), -// )?; -// } -// } -// } -// self.dynamic_context = dynamic_context; - -// Some(self) -// } - -// #[cfg(any(feature = "hot-reload", debug_assertions))] -// pub fn try_into_owned(self, location: &OwnedCodeLocation) -> Result { -// let mut nodes = Vec::new(); -// let dynamic_mapping = self.dynamic_mapping(&nodes); -// let dynamic_path = self.dynamic_path(); -// for node in self.nodes { -// nodes.push(node.try_into_owned(location)?); -// } - -// Ok(OwnedTemplate { -// nodes, -// root_nodes: self.root_nodes, -// dynamic_mapping, -// dynamic_path, -// }) -// } - -// #[cfg(any(feature = "hot-reload", debug_assertions))] -// pub fn dynamic_mapping( -// &self, -// resolved_nodes: &Vec, -// ) -> OwnedDynamicNodeMapping { -// let dynamic_context = &self.dynamic_context; -// let mut node_mapping = vec![None; dynamic_context.nodes.len()]; -// let nodes = &self.nodes; -// for n in nodes { -// if let TemplateNodeTypeBuilder::DynamicNode(idx) = &n.node_type { -// node_mapping[*idx] = Some(n.id) -// } -// } -// let mut text_mapping = vec![Vec::new(); dynamic_context.text.len()]; -// for n in nodes { -// if let TemplateNodeTypeBuilder::Text(txt) = &n.node_type { -// for seg in &txt.segments { -// match seg { -// TextTemplateSegment::Static(_) => (), -// TextTemplateSegment::Dynamic(idx) => text_mapping[*idx].push(n.id), -// } -// } -// } -// } -// let mut attribute_mapping = vec![Vec::new(); dynamic_context.attributes.len()]; -// for n in nodes { -// if let TemplateNodeTypeBuilder::Element(el) = &n.node_type { -// for (i, attr) in el.attributes.iter().enumerate() { -// match attr.value { -// TemplateAttributeValue::Static(_) => (), -// TemplateAttributeValue::Dynamic(idx) => { -// attribute_mapping[idx].push((n.id, i)); -// } -// } -// } -// } -// } -// let mut listener_mapping = Vec::new(); -// for n in nodes { -// if let TemplateNodeTypeBuilder::Element(el) = &n.node_type { -// if !el.listeners.is_empty() { -// listener_mapping.push(n.id); -// } -// } -// } - -// let mut volatile_mapping = Vec::new(); -// for n in resolved_nodes { -// if let TemplateNodeType::Element(el) = &n.node_type { -// for (i, attr) in el.attributes.iter().enumerate() { -// if attr.attribute.volatile { -// volatile_mapping.push((n.id, i)); -// } -// } -// } -// } - -// OwnedDynamicNodeMapping::new( -// node_mapping, -// text_mapping, -// attribute_mapping, -// volatile_mapping, -// listener_mapping, -// ) -// } - -// fn dynamic_path(&self) -> Option { -// let mut last_seg: Option = None; -// let mut nodes_to_insert_after = Vec::new(); -// // iterating from the last root to the first -// for root in self.root_nodes.iter().rev() { -// let root_node = &self.nodes[root.0]; -// if let TemplateNodeTypeBuilder::DynamicNode(_) = root_node.node_type { -// match &mut last_seg { -// // if there has been no static nodes, we can append the child to the parent node -// None => nodes_to_insert_after.push(*root), -// // otherwise we insert the child before the last static node -// Some(seg) => { -// seg.ops.push(UpdateOp::InsertBefore(*root)); -// } -// } -// } else if let Some(mut new) = self.construct_path_segment(*root) { -// if let Some(last) = last_seg.take() { -// match new.traverse { -// OwnedTraverse::Halt => { -// new.traverse = OwnedTraverse::NextSibling(Box::new(last)); -// } -// OwnedTraverse::FirstChild(b) => { -// new.traverse = OwnedTraverse::Both(Box::new((*b, last))); -// } -// _ => unreachable!(), -// } -// } else { -// for node in nodes_to_insert_after.drain(..) { -// new.ops.push(UpdateOp::InsertAfter(node)); -// } -// } -// last_seg = Some(new); -// } else if let Some(last) = last_seg.take() { -// last_seg = Some(OwnedPathSeg { -// ops: Vec::new(), -// traverse: OwnedTraverse::NextSibling(Box::new(last)), -// }); -// } -// } -// last_seg -// } - -// fn construct_path_segment(&self, node_id: TemplateNodeId) -> Option { -// let n = &self.nodes[node_id.0]; -// if n.fully_static { -// return None; -// } -// match &n.node_type { -// TemplateNodeTypeBuilder::Element(el) => { -// let mut last_seg: Option = None; -// let mut children_to_append = Vec::new(); -// // iterating from the last child to the first -// for child in el.children.iter().rev() { -// let child_node = &self.nodes[child.0]; -// if let TemplateNodeTypeBuilder::DynamicNode(_) = child_node.node_type { -// match &mut last_seg { -// // if there has been no static nodes, we can append the child to the parent node -// None => children_to_append.push(*child), -// // otherwise we insert the child before the last static node -// Some(seg) => { -// seg.ops.push(UpdateOp::InsertBefore(*child)); -// } -// } -// } else if let Some(mut new) = self.construct_path_segment(*child) { -// if let Some(last) = last_seg.take() { -// match new.traverse { -// OwnedTraverse::Halt => { -// new.traverse = OwnedTraverse::NextSibling(Box::new(last)); -// } -// OwnedTraverse::FirstChild(b) => { -// new.traverse = OwnedTraverse::Both(Box::new((*b, last))); -// } -// _ => unreachable!(), -// } -// } -// last_seg = Some(new); -// } else if let Some(last) = last_seg.take() { -// last_seg = Some(OwnedPathSeg { -// ops: Vec::new(), -// traverse: OwnedTraverse::NextSibling(Box::new(last)), -// }); -// } -// } -// let mut ops = Vec::new(); -// if !el.locally_static || n.parent.is_none() { -// ops.push(UpdateOp::StoreNode(node_id)); -// } -// for child in children_to_append.into_iter().rev() { -// ops.push(UpdateOp::AppendChild(child)); -// } -// Some(OwnedPathSeg { -// ops, -// traverse: match last_seg { -// Some(last) => OwnedTraverse::FirstChild(Box::new(last)), -// None => OwnedTraverse::Halt, -// }, -// }) -// } -// TemplateNodeTypeBuilder::Text(_) => Some(OwnedPathSeg { -// ops: vec![UpdateOp::StoreNode(n.id)], -// traverse: dioxus_core::OwnedTraverse::Halt, -// }), -// TemplateNodeTypeBuilder::DynamicNode(_) => unreachable!( -// "constructing path segment for dynamic nodes is handled in the parent node" -// ), -// } -// } -// } - -// impl ToTokens for TemplateBuilder { -// fn to_tokens(&self, tokens: &mut TokenStream) { -// let Self { -// nodes, -// root_nodes, -// dynamic_context, -// } = self; - -// let mut node_mapping = vec![None; dynamic_context.nodes.len()]; -// for n in nodes { -// if let TemplateNodeTypeBuilder::DynamicNode(idx) = &n.node_type { -// node_mapping[*idx] = Some(n.id); -// } -// } -// let mut text_mapping = vec![Vec::new(); dynamic_context.text.len()]; -// for n in nodes { -// if let TemplateNodeTypeBuilder::Text(txt) = &n.node_type { -// for seg in &txt.segments { -// match seg { -// TextTemplateSegment::Static(_) => (), -// TextTemplateSegment::Dynamic(idx) => text_mapping[*idx].push(n.id), -// } -// } -// } -// } -// let mut attribute_mapping = vec![Vec::new(); dynamic_context.attributes.len()]; -// for n in nodes { -// if let TemplateNodeTypeBuilder::Element(el) = &n.node_type { -// for (i, attr) in el.attributes.iter().enumerate() { -// match attr.value { -// TemplateAttributeValue::Static(_) => (), -// TemplateAttributeValue::Dynamic(idx) => { -// attribute_mapping[idx].push((n.id, i)); -// } -// } -// } -// } -// } -// let mut listener_mapping = Vec::new(); -// for n in nodes { -// if let TemplateNodeTypeBuilder::Element(el) = &n.node_type { -// if !el.listeners.is_empty() { -// listener_mapping.push(n.id); -// } -// } -// } - -// let root_nodes = root_nodes.iter().map(|id| { -// let raw = id.0; -// quote! { TemplateNodeId(#raw) } -// }); -// let node_mapping_quoted = node_mapping.iter().map(|op| match op { -// Some(id) => { -// let raw_id = id.0; -// quote! {Some(TemplateNodeId(#raw_id))} -// } -// None => quote! {None}, -// }); -// let text_mapping_quoted = text_mapping.iter().map(|inner| { -// let raw = inner.iter().map(|id| id.0); -// quote! {&[#(TemplateNodeId(#raw)),*]} -// }); -// let attribute_mapping_quoted = attribute_mapping.iter().map(|inner| { -// let raw = inner.iter().map(|(id, _)| id.0); -// let indecies = inner.iter().map(|(_, idx)| idx); -// quote! {&[#((TemplateNodeId(#raw), #indecies)),*]} -// }); -// let listener_mapping_quoted = listener_mapping.iter().map(|id| { -// let raw = id.0; -// quote! {TemplateNodeId(#raw)} -// }); -// let mut nodes_quoted = TokenStream::new(); -// for n in nodes { -// n.to_tokens(&mut nodes_quoted); -// quote! {,}.to_tokens(&mut nodes_quoted); -// } - -// let dynamic_path = match self.dynamic_path() { -// Some(seg) => { -// let seg = quote_owned_segment(seg); -// quote! {Some(#seg)} -// } -// None => quote! {None}, -// }; - -// let quoted = quote! { -// { -// const __NODES: dioxus::prelude::StaticTemplateNodes = &[#nodes_quoted]; -// const __TEXT_MAPPING: &'static [&'static [dioxus::prelude::TemplateNodeId]] = &[#(#text_mapping_quoted),*]; -// const __ATTRIBUTE_MAPPING: &'static [&'static [(dioxus::prelude::TemplateNodeId, usize)]] = &[#(#attribute_mapping_quoted),*]; -// const __ROOT_NODES: &'static [dioxus::prelude::TemplateNodeId] = &[#(#root_nodes),*]; -// const __NODE_MAPPING: &'static [Option] = &[#(#node_mapping_quoted),*]; -// const __NODES_WITH_LISTENERS: &'static [dioxus::prelude::TemplateNodeId] = &[#(#listener_mapping_quoted),*]; -// static __VOLITALE_MAPPING_INNER: dioxus::core::exports::once_cell::sync::Lazy> = dioxus::core::exports::once_cell::sync::Lazy::new(||{ -// // check each property to see if it is volatile -// let mut volatile = Vec::new(); -// for n in __NODES { -// if let TemplateNodeType::Element(el) = &n.node_type { -// for (i, attr) in el.attributes.iter().enumerate() { -// if attr.attribute.volatile { -// volatile.push((n.id, i)); -// } -// } -// } -// } -// volatile -// }); -// static __VOLITALE_MAPPING: &'static dioxus::core::exports::once_cell::sync::Lazy> = &__VOLITALE_MAPPING_INNER; -// static __STATIC_VOLITALE_MAPPING: dioxus::prelude::LazyStaticVec<(dioxus::prelude::TemplateNodeId, usize)> = LazyStaticVec(__VOLITALE_MAPPING); -// static __TEMPLATE: dioxus::prelude::Template = Template::Static(&StaticTemplate { -// nodes: __NODES, -// root_nodes: __ROOT_NODES, -// dynamic_mapping: StaticDynamicNodeMapping::new(__NODE_MAPPING, __TEXT_MAPPING, __ATTRIBUTE_MAPPING, __STATIC_VOLITALE_MAPPING, __NODES_WITH_LISTENERS), -// dynamic_path: #dynamic_path, -// }); - -// let __bump = __cx.bump(); -// __cx.template_ref(dioxus::prelude::TemplateId(get_line_num!()), __TEMPLATE.clone(), #dynamic_context) -// } -// }; - -// tokens.append_all(quoted) -// } -// } - -// #[derive(Default, Clone, Debug)] -// pub struct DynamicTemplateContextBuilder { -// nodes: Vec, -// text: Vec, -// attributes: Vec, -// listeners: Vec<(String, Expr)>, -// key: Option, -// } - -// impl DynamicTemplateContextBuilder { -// fn add_node(&mut self, node: BodyNode) -> usize { -// let node_id = self.nodes.len(); - -// self.nodes.push(node); - -// node_id -// } - -// fn add_text(&mut self, text: FormattedSegment) -> usize { -// let text_id = self.text.len(); - -// self.text.push(text); - -// text_id -// } - -// fn add_attr(&mut self, attr: TokenStream) -> usize { -// let attr_id = self.attributes.len(); - -// self.attributes.push(attr); - -// attr_id -// } - -// fn add_listener(&mut self, name: Ident, listener: Expr) -> usize { -// let listener_id = self.listeners.len(); - -// self.listeners.push((name.to_string(), listener)); - -// listener_id -// } - -// fn add_key(&mut self, key: TokenStream) { -// self.key = Some(key); -// } -// } - -// impl ToTokens for DynamicTemplateContextBuilder { -// fn to_tokens(&self, tokens: &mut TokenStream) { -// let nodes = &self.nodes; -// let text = &self.text; -// let attributes = &self.attributes; -// let listeners_names = self -// .listeners -// .iter() -// .map(|(n, _)| syn::parse_str::(n).expect(n)); -// let listeners_exprs = self.listeners.iter().map(|(_, e)| e); -// let key = match &self.key { -// Some(k) => quote!(Some(#k)), -// None => quote!(None), -// }; -// tokens.append_all(quote! { -// TemplateContext { -// nodes: __cx.bump().alloc([#(#nodes),*]), -// text_segments: __cx.bump().alloc([#(&*dioxus::core::exports::bumpalo::format!(in __bump, "{}", #text).into_bump_str()),*]), -// attributes: __cx.bump().alloc([#({#attributes}.into_value(__cx.bump())),*]), -// listeners: __cx.bump().alloc([#(dioxus_elements::on::#listeners_names(__cx, #listeners_exprs)),*]), -// key: #key, -// } -// }) -// } -// } - -// fn quote_owned_segment(seg: OwnedPathSeg) -> proc_macro2::TokenStream { -// let OwnedPathSeg { ops, traverse } = seg; - -// let ops = ops -// .into_iter() -// .map(|op| match op { -// UpdateOp::StoreNode(id) => { -// let id = quote_template_node_id(id); -// quote!(UpdateOp::StoreNode(#id)) -// } -// UpdateOp::InsertBefore(id) => { -// let id = quote_template_node_id(id); -// quote!(UpdateOp::InsertBefore(#id)) -// } -// UpdateOp::InsertAfter(id) => { -// let id = quote_template_node_id(id); -// quote!(UpdateOp::InsertAfter(#id)) -// } -// UpdateOp::AppendChild(id) => { -// let id = quote_template_node_id(id); -// quote!(UpdateOp::AppendChild(#id)) -// } -// }) -// .collect::>(); - -// let traverse = quote_owned_traverse(traverse); - -// quote! { -// StaticPathSeg { -// ops: &[#(#ops),*], -// traverse: #traverse, -// } -// } -// } - -// fn quote_owned_traverse(traverse: OwnedTraverse) -> proc_macro2::TokenStream { -// match traverse { -// OwnedTraverse::Halt => { -// quote! {StaticTraverse::Halt} -// } -// OwnedTraverse::FirstChild(seg) => { -// let seg = quote_owned_segment(*seg); -// quote! {StaticTraverse::FirstChild(&#seg)} -// } -// OwnedTraverse::NextSibling(seg) => { -// let seg = quote_owned_segment(*seg); -// quote! {StaticTraverse::NextSibling(&#seg)} -// } -// OwnedTraverse::Both(b) => { -// let (child, sibling) = *b; -// let child = quote_owned_segment(child); -// let sibling = quote_owned_segment(sibling); -// quote! {StaticTraverse::Both(&(#child, #sibling))} -// } -// } -// } - -// fn quote_template_node_id(id: TemplateNodeId) -> proc_macro2::TokenStream { -// let raw = id.0; -// quote! { -// TemplateNodeId(#raw) -// } -// } From 8a1c96a68cb5c46d366ac50d77afb9777482cbf4 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Fri, 9 Dec 2022 16:18:37 -0600 Subject: [PATCH 002/432] fix custom attribute value support --- packages/autofmt/src/buffer.rs | 4 +- packages/autofmt/src/element.rs | 2 +- packages/core/src/create.rs | 37 +++---- packages/core/src/diff.rs | 22 ++-- packages/core/src/lib.rs | 14 +-- packages/core/src/mutations.rs | 21 +--- packages/core/src/nodes.rs | 77 ++++++++++++-- packages/core/src/scopes.rs | 4 +- packages/core/src/virtual_dom.rs | 10 +- packages/core/tests/attr_cleanup.rs | 21 +++- packages/core/tests/boolattrs.rs | 11 +- packages/core/tests/borrowedstate.rs | 2 +- packages/core/tests/bubble_error.rs | 4 +- packages/core/tests/cycle.rs | 18 ++-- packages/core/tests/diff_component.rs | 28 ++--- packages/core/tests/diff_keyed_list.rs | 38 +++---- packages/core/tests/diff_unkeyed_list.rs | 126 ++++++++++++----------- packages/core/tests/kitchen_sink.rs | 18 ++-- packages/core/tests/suspense.rs | 50 ++++----- packages/dioxus/benches/jsframework.rs | 2 +- packages/native-core-macro/Cargo.toml | 3 +- packages/native-core-macro/src/lib.rs | 3 +- packages/native-core/src/node.rs | 41 ++++++-- packages/native-core/src/real_dom.rs | 54 ++-------- packages/rsx/src/element.rs | 6 +- packages/tui/src/focus.rs | 2 +- packages/web/src/dom.rs | 19 +++- packages/web/src/lib.rs | 8 +- 28 files changed, 369 insertions(+), 276 deletions(-) diff --git a/packages/autofmt/src/buffer.rs b/packages/autofmt/src/buffer.rs index 45799ca62..79ad7dfb7 100644 --- a/packages/autofmt/src/buffer.rs +++ b/packages/autofmt/src/buffer.rs @@ -144,8 +144,8 @@ impl Buffer { let mut total = 0; for attr in attributes { - if self.current_span_is_primary(attr.attr.flart()) { - 'line: for line in self.src[..attr.attr.flart().start().line - 1].iter().rev() { + if self.current_span_is_primary(attr.attr.start()) { + 'line: for line in self.src[..attr.attr.start().start().line - 1].iter().rev() { match (line.trim().starts_with("//"), line.is_empty()) { (true, _) => return 100000, (_, true) => continue 'line, diff --git a/packages/autofmt/src/element.rs b/packages/autofmt/src/element.rs index fd4529766..b5f1c2f84 100644 --- a/packages/autofmt/src/element.rs +++ b/packages/autofmt/src/element.rs @@ -157,7 +157,7 @@ impl Buffer { while let Some(attr) = attr_iter.next() { self.indent += 1; if !sameline { - self.write_comments(attr.attr.flart())?; + self.write_comments(attr.attr.start())?; } self.indent -= 1; diff --git a/packages/core/src/create.rs b/packages/core/src/create.rs index cc63a0e8b..4804897a2 100644 --- a/packages/core/src/create.rs +++ b/packages/core/src/create.rs @@ -105,27 +105,10 @@ impl<'b> VirtualDom { attribute.mounted_element.set(id); // Safety: we promise not to re-alias this text later on after committing it to the mutation - let unbounded_name = unsafe { std::mem::transmute(attribute.name) }; + let unbounded_name: &str = + unsafe { std::mem::transmute(attribute.name) }; match &attribute.value { - AttributeValue::Text(value) => { - // Safety: we promise not to re-alias this text later on after committing it to the mutation - let unbounded_value = unsafe { std::mem::transmute(*value) }; - - self.mutations.push(SetAttribute { - name: unbounded_name, - value: unbounded_value, - ns: attribute.namespace, - id, - }) - } - AttributeValue::Bool(value) => { - self.mutations.push(SetBoolAttribute { - name: unbounded_name, - value: *value, - id, - }) - } AttributeValue::Listener(_) => { self.mutations.push(NewEventListener { // all listeners start with "on" @@ -134,10 +117,18 @@ impl<'b> VirtualDom { id, }) } - AttributeValue::Float(_) => todo!(), - AttributeValue::Int(_) => todo!(), - AttributeValue::Any(_) => todo!(), - AttributeValue::None => todo!(), + _ => { + // Safety: we promise not to re-alias this text later on after committing it to the mutation + let unbounded_value = + unsafe { std::mem::transmute(attribute.value.clone()) }; + + self.mutations.push(SetAttribute { + name: unbounded_name, + value: unbounded_value, + ns: attribute.namespace, + id, + }) + } } // Only push the dynamic attributes forward if they match the current path (same element) diff --git a/packages/core/src/diff.rs b/packages/core/src/diff.rs index 0384192e2..b2d90a2ca 100644 --- a/packages/core/src/diff.rs +++ b/packages/core/src/diff.rs @@ -77,20 +77,14 @@ impl<'b> VirtualDom { if left_attr.value != right_attr.value || left_attr.volatile { // todo: add more types of attribute values - match right_attr.value { - AttributeValue::Text(text) => { - let name = unsafe { std::mem::transmute(left_attr.name) }; - let value = unsafe { std::mem::transmute(text) }; - self.mutations.push(Mutation::SetAttribute { - id: left_attr.mounted_element.get(), - ns: right_attr.namespace, - name, - value, - }); - } - // todo: more types of attribute values - _ => todo!("other attribute types"), - } + let name = unsafe { std::mem::transmute(left_attr.name) }; + let value = unsafe { std::mem::transmute(right_attr.value.clone()) }; + self.mutations.push(Mutation::SetAttribute { + id: left_attr.mounted_element.get(), + ns: right_attr.namespace, + name, + value, + }); } } diff --git a/packages/core/src/lib.rs b/packages/core/src/lib.rs index 613a4a115..0de74c911 100644 --- a/packages/core/src/lib.rs +++ b/packages/core/src/lib.rs @@ -70,10 +70,10 @@ pub(crate) mod innerlude { } pub use crate::innerlude::{ - fc_to_builder, Attribute, AttributeValue, Component, DynamicNode, Element, ElementId, Event, - Fragment, IntoDynNode, LazyNodes, Mutation, Mutations, Properties, RenderReturn, Scope, - ScopeId, ScopeState, Scoped, SuspenseContext, TaskId, Template, TemplateAttribute, - TemplateNode, VComponent, VNode, VText, VirtualDom, + fc_to_builder, AnyValueBox, Attribute, AttributeValue, Component, DynamicNode, Element, + ElementId, Event, Fragment, IntoAttributeValue, IntoDynNode, LazyNodes, Mutation, Mutations, + Properties, RenderReturn, Scope, ScopeId, ScopeState, Scoped, SuspenseContext, TaskId, + Template, TemplateAttribute, TemplateNode, VComponent, VNode, VText, VirtualDom, }; /// The purpose of this module is to alleviate imports of many common types @@ -81,9 +81,9 @@ pub use crate::innerlude::{ /// This includes types like [`Scope`], [`Element`], and [`Component`]. pub mod prelude { pub use crate::innerlude::{ - fc_to_builder, Element, Event, EventHandler, Fragment, LazyNodes, Properties, Scope, - ScopeId, ScopeState, Scoped, TaskId, Template, TemplateAttribute, TemplateNode, VNode, - VirtualDom, + fc_to_builder, Element, Event, EventHandler, Fragment, IntoAttributeValue, LazyNodes, + Properties, Scope, ScopeId, ScopeState, Scoped, TaskId, Template, TemplateAttribute, + TemplateNode, VNode, VirtualDom, }; } diff --git a/packages/core/src/mutations.rs b/packages/core/src/mutations.rs index 2512ae9fa..938458a3d 100644 --- a/packages/core/src/mutations.rs +++ b/packages/core/src/mutations.rs @@ -1,6 +1,6 @@ use fxhash::FxHashSet; -use crate::{arena::ElementId, ScopeId, Template}; +use crate::{arena::ElementId, AttributeValue, ScopeId, Template}; /// A container for all the relevant steps to modify the Real DOM /// @@ -48,7 +48,7 @@ impl<'a> Mutations<'a> { /// Push a new mutation into the dom_edits list pub(crate) fn push(&mut self, mutation: Mutation<'static>) { - self.edits.push(mutation) + unsafe { self.edits.push(std::mem::transmute(mutation)) } } } @@ -61,7 +61,7 @@ impl<'a> Mutations<'a> { derive(serde::Serialize, serde::Deserialize), serde(tag = "type") )] -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq)] pub enum Mutation<'a> { /// Add these m children to the target element AppendChildren { @@ -193,8 +193,9 @@ pub enum Mutation<'a> { SetAttribute { /// The name of the attribute to set. name: &'a str, + /// The value of the attribute. - value: &'a str, + value: AttributeValue<'a>, /// The ID of the node to set the attribute of. id: ElementId, @@ -204,18 +205,6 @@ pub enum Mutation<'a> { ns: Option<&'a str>, }, - /// Set the value of a node's attribute. - SetBoolAttribute { - /// The name of the attribute to set. - name: &'a str, - - /// The value of the attribute. - value: bool, - - /// The ID of the node to set the attribute of. - id: ElementId, - }, - /// Set the textcontent of a node. SetText { /// The textcontent of the node diff --git a/packages/core/src/nodes.rs b/packages/core/src/nodes.rs index 81ed5da06..c776da967 100644 --- a/packages/core/src/nodes.rs +++ b/packages/core/src/nodes.rs @@ -8,6 +8,7 @@ use std::{ cell::{Cell, RefCell}, fmt::Arguments, future::Future, + rc::Rc, }; pub type TemplateId = &'static str; @@ -88,7 +89,7 @@ impl<'a> VNode<'a> { pub(crate) fn clear_listeners(&self) { for attr in self.dynamic_attrs { if let AttributeValue::Listener(l) = &attr.value { - l.borrow_mut().take(); + l.0.borrow_mut().take(); } } } @@ -317,6 +318,9 @@ pub struct Attribute<'a> { /// /// These are built-in to be faster during the diffing process. To use a custom value, use the [`AttributeValue::Any`] /// variant. +#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serialize", serde(untagged))] +#[derive(Clone)] pub enum AttributeValue<'a> { /// Text attribute Text(&'a str), @@ -331,16 +335,67 @@ pub enum AttributeValue<'a> { Bool(bool), /// A listener, like "onclick" - Listener(RefCell>>), + Listener(ListenerCb<'a>), /// An arbitrary value that implements PartialEq and is static - Any(BumpBox<'a, dyn AnyValue>), + Any(AnyValueBox), /// A "none" value, resulting in the removal of an attribute from the dom None, } -type ListenerCb<'a> = BumpBox<'a, dyn FnMut(Event) + 'a>; +pub type ListenerCbInner<'a> = RefCell) + 'a>>>; +pub struct ListenerCb<'a>(pub ListenerCbInner<'a>); + +impl Clone for ListenerCb<'_> { + fn clone(&self) -> Self { + panic!("ListenerCb cannot be cloned") + } +} + +#[cfg(feature = "serialize")] +impl<'a> serde::Serialize for ListenerCb<'a> { + fn serialize(&self, _: S) -> Result + where + S: serde::Serializer, + { + panic!("ListenerCb cannot be serialized") + } +} + +#[cfg(feature = "serialize")] +impl<'de, 'a> serde::Deserialize<'de> for ListenerCb<'a> { + fn deserialize(_: D) -> Result + where + D: serde::Deserializer<'de>, + { + panic!("ListenerCb cannot be deserialized") + } +} + +/// A boxed value that implements PartialEq and Any +#[derive(Clone)] +pub struct AnyValueBox(pub Rc); + +#[cfg(feature = "serialize")] +impl serde::Serialize for AnyValueBox { + fn serialize(&self, _: S) -> Result + where + S: serde::Serializer, + { + panic!("AnyValueBox cannot be serialized") + } +} + +#[cfg(feature = "serialize")] +impl<'de> serde::Deserialize<'de> for AnyValueBox { + fn deserialize(_: D) -> Result + where + D: serde::Deserializer<'de>, + { + panic!("AnyValueBox cannot be deserialized") + } +} impl<'a> std::fmt::Debug for AttributeValue<'a> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -364,8 +419,8 @@ impl<'a> PartialEq for AttributeValue<'a> { (Self::Int(l0), Self::Int(r0)) => l0 == r0, (Self::Bool(l0), Self::Bool(r0)) => l0 == r0, (Self::Listener(_), Self::Listener(_)) => true, - (Self::Any(l0), Self::Any(r0)) => l0.any_cmp(r0.as_ref()), - _ => core::mem::discriminant(self) == core::mem::discriminant(other), + (Self::Any(l0), Self::Any(r0)) => l0.0.any_cmp(r0.0.as_ref()), + _ => false, } } } @@ -559,21 +614,25 @@ impl<'a> IntoAttributeValue<'a> for &'a str { AttributeValue::Text(self) } } + impl<'a> IntoAttributeValue<'a> for f64 { fn into_value(self, _: &'a Bump) -> AttributeValue<'a> { AttributeValue::Float(self) } } + impl<'a> IntoAttributeValue<'a> for i64 { fn into_value(self, _: &'a Bump) -> AttributeValue<'a> { AttributeValue::Int(self) } } + impl<'a> IntoAttributeValue<'a> for bool { fn into_value(self, _: &'a Bump) -> AttributeValue<'a> { AttributeValue::Bool(self) } } + impl<'a> IntoAttributeValue<'a> for Arguments<'_> { fn into_value(self, bump: &'a Bump) -> AttributeValue<'a> { use bumpalo::core_alloc::fmt::Write; @@ -582,3 +641,9 @@ impl<'a> IntoAttributeValue<'a> for Arguments<'_> { AttributeValue::Text(str_buf.into_bump_str()) } } + +impl<'a> IntoAttributeValue<'a> for AnyValueBox { + fn into_value(self, _: &'a Bump) -> AttributeValue<'a> { + AttributeValue::Any(self) + } +} diff --git a/packages/core/src/scopes.rs b/packages/core/src/scopes.rs index b1bbb42fa..31ac50440 100644 --- a/packages/core/src/scopes.rs +++ b/packages/core/src/scopes.rs @@ -4,7 +4,7 @@ use crate::{ arena::ElementId, bump_frame::BumpFrame, innerlude::{DynamicNode, EventHandler, VComponent, VText}, - innerlude::{Scheduler, SchedulerMsg}, + innerlude::{ListenerCb, Scheduler, SchedulerMsg}, lazynodes::LazyNodes, nodes::{ComponentReturn, IntoAttributeValue, IntoDynNode, RenderReturn}, Attribute, AttributeValue, Element, Event, Properties, TaskId, @@ -483,7 +483,7 @@ impl<'src> ScopeState { })) }; - AttributeValue::Listener(RefCell::new(Some(boxed))) + AttributeValue::Listener(ListenerCb(RefCell::new(Some(boxed)))) } /// Store a value between renders. The foundational hook for all other hooks. diff --git a/packages/core/src/virtual_dom.rs b/packages/core/src/virtual_dom.rs index ced92acb0..4c01512e2 100644 --- a/packages/core/src/virtual_dom.rs +++ b/packages/core/src/virtual_dom.rs @@ -386,7 +386,7 @@ impl VirtualDom { // We check the bubble state between each call to see if the event has been stopped from bubbling for listener in listeners.drain(..).rev() { if let AttributeValue::Listener(listener) = listener { - if let Some(cb) = listener.borrow_mut().as_deref_mut() { + if let Some(cb) = listener.0.borrow_mut().as_deref_mut() { cb(uievent.clone()); } @@ -493,7 +493,7 @@ impl VirtualDom { RenderReturn::Async(_) => unreachable!("Root scope cannot be an async component"), } - self.finalize() + unsafe { std::mem::transmute(self.finalize()) } } /// Render whatever the VirtualDom has ready as fast as possible without requiring an executor to progress @@ -591,7 +591,7 @@ impl VirtualDom { // If there's no pending suspense, then we have no reason to wait for anything if self.scheduler.leaves.borrow().is_empty() { - return self.finalize(); + return unsafe { std::mem::transmute(self.finalize()) }; } // Poll the suspense leaves in the meantime @@ -605,13 +605,13 @@ impl VirtualDom { if let Either::Left((_, _)) = select(&mut deadline, pinned).await { // release the borrowed drop(work); - return self.finalize(); + return unsafe { std::mem::transmute(self.finalize()) }; } } } /// Swap the current mutations with a new - fn finalize(&mut self) -> Mutations { + fn finalize(&mut self) -> Mutations<'static> { // todo: make this a routine let mut out = Mutations::default(); std::mem::swap(&mut self.mutations, &mut out); diff --git a/packages/core/tests/attr_cleanup.rs b/packages/core/tests/attr_cleanup.rs index c0aa9a1da..c8e1baa1f 100644 --- a/packages/core/tests/attr_cleanup.rs +++ b/packages/core/tests/attr_cleanup.rs @@ -2,6 +2,7 @@ //! //! This tests to ensure we clean it up +use bumpalo::Bump; use dioxus::core::{ElementId, Mutation::*}; use dioxus::prelude::*; @@ -22,6 +23,8 @@ fn attrs_cycle() { } }); + let bump = Bump::new(); + assert_eq!( dom.rebuild().santize().edits, [ @@ -36,8 +39,13 @@ fn attrs_cycle() { [ LoadTemplate { name: "template", index: 0, id: ElementId(2,) }, AssignId { path: &[0,], id: ElementId(3,) }, - SetAttribute { name: "class", value: "1", id: ElementId(3,), ns: None }, - SetAttribute { name: "id", value: "1", id: ElementId(3,), ns: None }, + SetAttribute { + name: "class", + value: "1".into_value(&bump), + id: ElementId(3,), + ns: None + }, + SetAttribute { name: "id", value: "1".into_value(&bump), id: ElementId(3,), ns: None }, ReplaceWith { id: ElementId(1,), m: 1 }, ] ); @@ -57,8 +65,13 @@ fn attrs_cycle() { [ LoadTemplate { name: "template", index: 0, id: ElementId(2) }, AssignId { path: &[0], id: ElementId(1) }, - SetAttribute { name: "class", value: "3", id: ElementId(1), ns: None }, - SetAttribute { name: "id", value: "3", id: ElementId(1), ns: None }, + SetAttribute { + name: "class", + value: "3".into_value(&bump), + id: ElementId(1), + ns: None + }, + SetAttribute { name: "id", value: "3".into_value(&bump), id: ElementId(1), ns: None }, ReplaceWith { id: ElementId(3), m: 1 } ] ); diff --git a/packages/core/tests/boolattrs.rs b/packages/core/tests/boolattrs.rs index 5cd2ad1ad..c2ad70333 100644 --- a/packages/core/tests/boolattrs.rs +++ b/packages/core/tests/boolattrs.rs @@ -1,15 +1,22 @@ +use bumpalo::Bump; use dioxus::core::{ElementId, Mutation::*}; use dioxus::prelude::*; #[test] fn bool_test() { let mut app = VirtualDom::new(|cx| cx.render(rsx!(div { hidden: false }))); + let bump = Bump::new(); assert_eq!( app.rebuild().santize().edits, [ LoadTemplate { name: "template", index: 0, id: ElementId(1) }, - SetBoolAttribute { name: "hidden", value: false, id: ElementId(1,) }, + SetAttribute { + name: "hidden", + value: false.into_value(&bump), + id: ElementId(1,), + ns: None + }, AppendChildren { m: 1, id: ElementId(0) }, ] - ) + ); } diff --git a/packages/core/tests/borrowedstate.rs b/packages/core/tests/borrowedstate.rs index 0aace9c97..1b83789ec 100644 --- a/packages/core/tests/borrowedstate.rs +++ b/packages/core/tests/borrowedstate.rs @@ -18,7 +18,7 @@ fn test_borrowed_state() { ReplacePlaceholder { path: &[0,], m: 1 }, AppendChildren { m: 1, id: ElementId(0) }, ] - ) + ); } fn Parent(cx: Scope) -> Element { diff --git a/packages/core/tests/bubble_error.rs b/packages/core/tests/bubble_error.rs index 3dbfe80a9..ebbbc613c 100644 --- a/packages/core/tests/bubble_error.rs +++ b/packages/core/tests/bubble_error.rs @@ -20,7 +20,9 @@ fn app(cx: Scope) -> Element { fn bubbles_error() { let mut dom = VirtualDom::new(app); - let _edits = dom.rebuild().santize(); + { + let _edits = dom.rebuild().santize(); + } dom.mark_dirty(ScopeId(0)); diff --git a/packages/core/tests/cycle.rs b/packages/core/tests/cycle.rs index 133b04658..10498f5cc 100644 --- a/packages/core/tests/cycle.rs +++ b/packages/core/tests/cycle.rs @@ -12,14 +12,16 @@ fn cycling_elements() { }) }); - let edits = dom.rebuild().santize(); - assert_eq!( - edits.edits, - [ - LoadTemplate { name: "template", index: 0, id: ElementId(1,) }, - AppendChildren { m: 1, id: ElementId(0) }, - ] - ); + { + let edits = dom.rebuild().santize(); + assert_eq!( + edits.edits, + [ + LoadTemplate { name: "template", index: 0, id: ElementId(1,) }, + AppendChildren { m: 1, id: ElementId(0) }, + ] + ); + } dom.mark_dirty(ScopeId(0)); assert_eq!( diff --git a/packages/core/tests/diff_component.rs b/packages/core/tests/diff_component.rs index 2b74f8302..69ab6de6d 100644 --- a/packages/core/tests/diff_component.rs +++ b/packages/core/tests/diff_component.rs @@ -59,19 +59,21 @@ fn component_swap() { } let mut dom = VirtualDom::new(app); - let edits = dom.rebuild().santize(); - assert_eq!( - edits.edits, - [ - LoadTemplate { name: "template", index: 0, id: ElementId(1) }, - LoadTemplate { name: "template", index: 0, id: ElementId(2) }, - LoadTemplate { name: "template", index: 0, id: ElementId(3) }, - LoadTemplate { name: "template", index: 0, id: ElementId(4) }, - ReplacePlaceholder { path: &[1], m: 3 }, - LoadTemplate { name: "template", index: 0, id: ElementId(5) }, - AppendChildren { m: 2, id: ElementId(0) } - ] - ); + { + let edits = dom.rebuild().santize(); + assert_eq!( + edits.edits, + [ + LoadTemplate { name: "template", index: 0, id: ElementId(1) }, + LoadTemplate { name: "template", index: 0, id: ElementId(2) }, + LoadTemplate { name: "template", index: 0, id: ElementId(3) }, + LoadTemplate { name: "template", index: 0, id: ElementId(4) }, + ReplacePlaceholder { path: &[1], m: 3 }, + LoadTemplate { name: "template", index: 0, id: ElementId(5) }, + AppendChildren { m: 2, id: ElementId(0) } + ] + ); + } dom.mark_dirty(ScopeId(0)); assert_eq!( diff --git a/packages/core/tests/diff_keyed_list.rs b/packages/core/tests/diff_keyed_list.rs index 81013849d..be26e00c0 100644 --- a/packages/core/tests/diff_keyed_list.rs +++ b/packages/core/tests/diff_keyed_list.rs @@ -20,22 +20,24 @@ fn keyed_diffing_out_of_order() { cx.render(rsx!(order.iter().map(|i| rsx!(div { key: "{i}" })))) }); - assert_eq!( - dom.rebuild().santize().edits, - [ - LoadTemplate { name: "template", index: 0, id: ElementId(1,) }, - LoadTemplate { name: "template", index: 0, id: ElementId(2,) }, - LoadTemplate { name: "template", index: 0, id: ElementId(3,) }, - LoadTemplate { name: "template", index: 0, id: ElementId(4,) }, - LoadTemplate { name: "template", index: 0, id: ElementId(5,) }, - LoadTemplate { name: "template", index: 0, id: ElementId(6,) }, - LoadTemplate { name: "template", index: 0, id: ElementId(7,) }, - LoadTemplate { name: "template", index: 0, id: ElementId(8,) }, - LoadTemplate { name: "template", index: 0, id: ElementId(9,) }, - LoadTemplate { name: "template", index: 0, id: ElementId(10,) }, - AppendChildren { m: 10, id: ElementId(0) }, - ] - ); + { + assert_eq!( + dom.rebuild().santize().edits, + [ + LoadTemplate { name: "template", index: 0, id: ElementId(1,) }, + LoadTemplate { name: "template", index: 0, id: ElementId(2,) }, + LoadTemplate { name: "template", index: 0, id: ElementId(3,) }, + LoadTemplate { name: "template", index: 0, id: ElementId(4,) }, + LoadTemplate { name: "template", index: 0, id: ElementId(5,) }, + LoadTemplate { name: "template", index: 0, id: ElementId(6,) }, + LoadTemplate { name: "template", index: 0, id: ElementId(7,) }, + LoadTemplate { name: "template", index: 0, id: ElementId(8,) }, + LoadTemplate { name: "template", index: 0, id: ElementId(9,) }, + LoadTemplate { name: "template", index: 0, id: ElementId(10,) }, + AppendChildren { m: 10, id: ElementId(0) }, + ] + ); + } dom.mark_dirty(ScopeId(0)); assert_eq!( @@ -44,7 +46,7 @@ fn keyed_diffing_out_of_order() { PushRoot { id: ElementId(7,) }, InsertBefore { id: ElementId(5,), m: 1 }, ] - ) + ); } /// Should result in moves only @@ -70,7 +72,7 @@ fn keyed_diffing_out_of_order_adds() { PushRoot { id: ElementId(4,) }, InsertBefore { id: ElementId(1,), m: 2 }, ] - ) + ); } /// Should result in moves only diff --git a/packages/core/tests/diff_unkeyed_list.rs b/packages/core/tests/diff_unkeyed_list.rs index 415aa79c0..84fd65604 100644 --- a/packages/core/tests/diff_unkeyed_list.rs +++ b/packages/core/tests/diff_unkeyed_list.rs @@ -314,66 +314,76 @@ fn remove_many() { }) }); - let edits = dom.rebuild().santize(); - assert!(edits.templates.is_empty()); - assert_eq!( - edits.edits, - [ - CreatePlaceholder { id: ElementId(1,) }, - AppendChildren { id: ElementId(0), m: 1 }, - ] - ); + { + let edits = dom.rebuild().santize(); + assert!(edits.templates.is_empty()); + assert_eq!( + edits.edits, + [ + CreatePlaceholder { id: ElementId(1,) }, + AppendChildren { id: ElementId(0), m: 1 }, + ] + ); + } - dom.mark_dirty(ScopeId(0)); - let edits = dom.render_immediate().santize(); - assert_eq!( - edits.edits, - [ - LoadTemplate { name: "template", index: 0, id: ElementId(2,) }, - HydrateText { path: &[0,], value: "hello 0", id: ElementId(3,) }, - ReplaceWith { id: ElementId(1,), m: 1 }, - ] - ); + { + dom.mark_dirty(ScopeId(0)); + let edits = dom.render_immediate().santize(); + assert_eq!( + edits.edits, + [ + LoadTemplate { name: "template", index: 0, id: ElementId(2,) }, + HydrateText { path: &[0,], value: "hello 0", id: ElementId(3,) }, + ReplaceWith { id: ElementId(1,), m: 1 }, + ] + ); + } - dom.mark_dirty(ScopeId(0)); - let edits = dom.render_immediate().santize(); - assert_eq!( - edits.edits, - [ - LoadTemplate { name: "template", index: 0, id: ElementId(1,) }, - HydrateText { path: &[0,], value: "hello 1", id: ElementId(4,) }, - LoadTemplate { name: "template", index: 0, id: ElementId(5,) }, - HydrateText { path: &[0,], value: "hello 2", id: ElementId(6,) }, - LoadTemplate { name: "template", index: 0, id: ElementId(7,) }, - HydrateText { path: &[0,], value: "hello 3", id: ElementId(8,) }, - LoadTemplate { name: "template", index: 0, id: ElementId(9,) }, - HydrateText { path: &[0,], value: "hello 4", id: ElementId(10,) }, - InsertAfter { id: ElementId(2,), m: 4 }, - ] - ); + { + dom.mark_dirty(ScopeId(0)); + let edits = dom.render_immediate().santize(); + assert_eq!( + edits.edits, + [ + LoadTemplate { name: "template", index: 0, id: ElementId(1,) }, + HydrateText { path: &[0,], value: "hello 1", id: ElementId(4,) }, + LoadTemplate { name: "template", index: 0, id: ElementId(5,) }, + HydrateText { path: &[0,], value: "hello 2", id: ElementId(6,) }, + LoadTemplate { name: "template", index: 0, id: ElementId(7,) }, + HydrateText { path: &[0,], value: "hello 3", id: ElementId(8,) }, + LoadTemplate { name: "template", index: 0, id: ElementId(9,) }, + HydrateText { path: &[0,], value: "hello 4", id: ElementId(10,) }, + InsertAfter { id: ElementId(2,), m: 4 }, + ] + ); + } - dom.mark_dirty(ScopeId(0)); - let edits = dom.render_immediate().santize(); - assert_eq!( - edits.edits, - [ - Remove { id: ElementId(9,) }, - Remove { id: ElementId(7,) }, - Remove { id: ElementId(5,) }, - Remove { id: ElementId(1,) }, - CreatePlaceholder { id: ElementId(3,) }, - ReplaceWith { id: ElementId(2,), m: 1 }, - ] - ); + { + dom.mark_dirty(ScopeId(0)); + let edits = dom.render_immediate().santize(); + assert_eq!( + edits.edits, + [ + Remove { id: ElementId(9,) }, + Remove { id: ElementId(7,) }, + Remove { id: ElementId(5,) }, + Remove { id: ElementId(1,) }, + CreatePlaceholder { id: ElementId(3,) }, + ReplaceWith { id: ElementId(2,), m: 1 }, + ] + ); + } - dom.mark_dirty(ScopeId(0)); - let edits = dom.render_immediate().santize(); - assert_eq!( - edits.edits, - [ - LoadTemplate { name: "template", index: 0, id: ElementId(2,) }, - HydrateText { path: &[0,], value: "hello 0", id: ElementId(1,) }, - ReplaceWith { id: ElementId(3,), m: 1 }, - ] - ) + { + dom.mark_dirty(ScopeId(0)); + let edits = dom.render_immediate().santize(); + assert_eq!( + edits.edits, + [ + LoadTemplate { name: "template", index: 0, id: ElementId(2,) }, + HydrateText { path: &[0,], value: "hello 0", id: ElementId(1,) }, + ReplaceWith { id: ElementId(3,), m: 1 }, + ] + ) + } } diff --git a/packages/core/tests/kitchen_sink.rs b/packages/core/tests/kitchen_sink.rs index 78ef608cc..1201f4e91 100644 --- a/packages/core/tests/kitchen_sink.rs +++ b/packages/core/tests/kitchen_sink.rs @@ -1,3 +1,4 @@ +use bumpalo::Bump; use dioxus::core::{ElementId, Mutation}; use dioxus::prelude::*; @@ -26,17 +27,22 @@ fn basic_syntax_is_a_template(cx: Scope) -> Element { #[test] fn dual_stream() { let mut dom = VirtualDom::new(basic_syntax_is_a_template); + let bump = Bump::new(); let edits = dom.rebuild().santize(); use Mutation::*; - assert_eq!( - edits.edits, + assert_eq!(edits.edits, { [ LoadTemplate { name: "template", index: 0, id: ElementId(1) }, - SetAttribute { name: "class", value: "123", id: ElementId(1), ns: None }, + SetAttribute { + name: "class", + value: "123".into_value(&bump), + id: ElementId(1), + ns: None, + }, NewEventListener { name: "click", scope: ScopeId(0), id: ElementId(1) }, HydrateText { path: &[0, 0], value: "123", id: ElementId(2) }, - AppendChildren { id: ElementId(0), m: 1 } - ], - ); + AppendChildren { id: ElementId(0), m: 1 }, + ] + }); } diff --git a/packages/core/tests/suspense.rs b/packages/core/tests/suspense.rs index 9fac13937..775ca3d71 100644 --- a/packages/core/tests/suspense.rs +++ b/packages/core/tests/suspense.rs @@ -9,32 +9,34 @@ use std::time::Duration; async fn it_works() { let mut dom = VirtualDom::new(app); - let mutations = dom.rebuild().santize(); + { + 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 } - // ] - // ); + // 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) }, - ] - ); + // 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) }, + ] + ); + } // wait just a moment, not enough time for the boundary to resolve diff --git a/packages/dioxus/benches/jsframework.rs b/packages/dioxus/benches/jsframework.rs index 24ca2fe69..b3405b2bd 100644 --- a/packages/dioxus/benches/jsframework.rs +++ b/packages/dioxus/benches/jsframework.rs @@ -42,7 +42,7 @@ fn create_rows(c: &mut Criterion) { c.bench_function("create rows", |b| { let mut dom = VirtualDom::new(app); - dom.rebuild(); + let _ = dom.rebuild(); b.iter(|| { let g = dom.rebuild(); diff --git a/packages/native-core-macro/Cargo.toml b/packages/native-core-macro/Cargo.toml index bff83ec42..759d8ff00 100644 --- a/packages/native-core-macro/Cargo.toml +++ b/packages/native-core-macro/Cargo.toml @@ -18,10 +18,9 @@ proc-macro = true syn = { version = "1.0.11", features = ["extra-traits"] } quote = "1.0" dioxus-native-core = { path = "../native-core", version = "^0.2.0" } - -[dev-dependencies] dioxus = { path = "../dioxus" } +[dev-dependencies] smallvec = "1.6" rustc-hash = "1.1.0" anymap = "0.12.1" diff --git a/packages/native-core-macro/src/lib.rs b/packages/native-core-macro/src/lib.rs index dda96b512..5bf50afcc 100644 --- a/packages/native-core-macro/src/lib.rs +++ b/packages/native-core-macro/src/lib.rs @@ -306,8 +306,7 @@ impl Member { + field.ty.to_token_stream().to_string().as_str()) .as_str(), Span::call_site(), - ) - .into(), + ), ident: field.ident.as_ref()?.clone(), }) } diff --git a/packages/native-core/src/node.rs b/packages/native-core/src/node.rs index 4530fd350..7220e0fab 100644 --- a/packages/native-core/src/node.rs +++ b/packages/native-core/src/node.rs @@ -1,6 +1,7 @@ use crate::{state::State, tree::NodeId}; -use dioxus_core::ElementId; +use dioxus_core::{AnyValueBox, AttributeValue, ElementId}; use rustc_hash::{FxHashMap, FxHashSet}; +use std::fmt::Debug; /// The node is stored client side and stores only basic data about the node. #[derive(Debug, Clone)] @@ -72,15 +73,43 @@ pub struct OwnedAttributeView<'a> { pub value: &'a OwnedAttributeValue, } -#[derive(Clone, Debug)] +#[derive(Clone)] pub enum OwnedAttributeValue { Text(String), - Float(f32), - Int(i32), + Float(f64), + Int(i64), Bool(bool), + Any(AnyValueBox), None, } +impl Debug for OwnedAttributeValue { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Text(arg0) => f.debug_tuple("Text").field(arg0).finish(), + Self::Float(arg0) => f.debug_tuple("Float").field(arg0).finish(), + Self::Int(arg0) => f.debug_tuple("Int").field(arg0).finish(), + Self::Bool(arg0) => f.debug_tuple("Bool").field(arg0).finish(), + Self::Any(_) => f.debug_tuple("Any").finish(), + Self::None => write!(f, "None"), + } + } +} + +impl From> for OwnedAttributeValue { + fn from(value: AttributeValue<'_>) -> Self { + match value { + AttributeValue::Text(text) => Self::Text(text.to_string()), + AttributeValue::Float(float) => Self::Float(float), + AttributeValue::Int(int) => Self::Int(int), + AttributeValue::Bool(bool) => Self::Bool(bool), + AttributeValue::Any(any) => Self::Any(any), + AttributeValue::None => Self::None, + _ => Self::None, + } + } +} + impl OwnedAttributeValue { pub fn as_text(&self) -> Option<&str> { match self { @@ -89,14 +118,14 @@ impl OwnedAttributeValue { } } - pub fn as_float(&self) -> Option { + pub fn as_float(&self) -> Option { match self { OwnedAttributeValue::Float(float) => Some(*float), _ => None, } } - pub fn as_int(&self) -> Option { + pub fn as_int(&self) -> Option { match self { OwnedAttributeValue::Int(int) => Some(*int), _ => None, diff --git a/packages/native-core/src/real_dom.rs b/packages/native-core/src/real_dom.rs index 7d56b0dca..637a8d7ce 100644 --- a/packages/native-core/src/real_dom.rs +++ b/packages/native-core/src/real_dom.rs @@ -102,11 +102,7 @@ impl RealDom { self.tree.add_child(node_id, child_id); } - fn create_template_node( - &mut self, - node: &TemplateNode, - mutations_vec: &mut FxHashMap, - ) -> RealNodeId { + fn create_template_node(&mut self, node: &TemplateNode) -> RealNodeId { match node { TemplateNode::Element { tag, @@ -139,27 +135,18 @@ impl RealDom { }); let node_id = self.create_node(node); for child in *children { - let child_id = self.create_template_node(child, mutations_vec); + let child_id = self.create_template_node(child); self.add_child(node_id, child_id); } node_id } - TemplateNode::Text { text } => { - let node_id = self.create_node(Node::new(NodeType::Text { - text: text.to_string(), - })); - node_id - } - TemplateNode::Dynamic { .. } => { - let node_id = self.create_node(Node::new(NodeType::Placeholder)); - node_id - } - TemplateNode::DynamicText { .. } => { - let node_id = self.create_node(Node::new(NodeType::Text { - text: String::new(), - })); - node_id - } + TemplateNode::Text { text } => self.create_node(Node::new(NodeType::Text { + text: text.to_string(), + })), + TemplateNode::Dynamic { .. } => self.create_node(Node::new(NodeType::Placeholder)), + TemplateNode::DynamicText { .. } => self.create_node(Node::new(NodeType::Text { + text: String::new(), + })), } } @@ -172,7 +159,7 @@ impl RealDom { for template in mutations.templates { let mut template_root_ids = Vec::new(); for root in template.roots { - let id = self.create_template_node(root, &mut nodes_updated); + let id = self.create_template_node(root); template_root_ids.push(id); } self.templates @@ -283,26 +270,7 @@ impl RealDom { namespace: ns.map(|s| s.to_string()), volatile: false, }, - crate::node::OwnedAttributeValue::Text(value.to_string()), - ); - mark_dirty( - node_id, - NodeMask::new_with_attrs(AttributeMask::single(name)), - &mut nodes_updated, - ); - } - } - SetBoolAttribute { name, value, id } => { - let node_id = self.element_to_node_id(id); - let node = self.tree.get_mut(node_id).unwrap(); - if let NodeType::Element { attributes, .. } = &mut node.node_data.node_type { - attributes.insert( - OwnedAttributeDiscription { - name: name.to_string(), - namespace: None, - volatile: false, - }, - crate::node::OwnedAttributeValue::Bool(value), + OwnedAttributeValue::from(value), ); mark_dirty( node_id, diff --git a/packages/rsx/src/element.rs b/packages/rsx/src/element.rs index 307059f9d..88699b79e 100644 --- a/packages/rsx/src/element.rs +++ b/packages/rsx/src/element.rs @@ -211,7 +211,7 @@ pub enum ElementAttr { } impl ElementAttr { - pub fn flart(&self) -> Span { + pub fn start(&self) -> Span { match self { ElementAttr::AttrText { name, .. } => name.span(), ElementAttr::AttrExpression { name, .. } => name.span(), @@ -265,7 +265,7 @@ impl ToTokens for ElementAttrNamed { ElementAttr::CustomAttrText { name, value } => { quote! { __cx.attr( - dioxus_elements::#el_name::#name.0, + #name, #value, None, false @@ -275,7 +275,7 @@ impl ToTokens for ElementAttrNamed { ElementAttr::CustomAttrExpression { name, value } => { quote! { __cx.attr( - dioxus_elements::#el_name::#name.0, + #name, #value, None, false diff --git a/packages/tui/src/focus.rs b/packages/tui/src/focus.rs index 90d9e9da0..57f1c9e1f 100644 --- a/packages/tui/src/focus.rs +++ b/packages/tui/src/focus.rs @@ -79,7 +79,7 @@ impl NodeDepState for Focus { if let Some(index) = a .value .as_int() - .or_else(|| a.value.as_text().and_then(|v| v.parse::().ok())) + .or_else(|| a.value.as_text().and_then(|v| v.parse::().ok())) { match index.cmp(&0) { Ordering::Less => FocusLevel::Unfocusable, diff --git a/packages/web/src/dom.rs b/packages/web/src/dom.rs index 0cdf46b3e..96d95bc89 100644 --- a/packages/web/src/dom.rs +++ b/packages/web/src/dom.rs @@ -75,10 +75,21 @@ impl WebsysDom { value, id, ns, - } => i.SetAttribute(id.0 as u32, name, value.into(), ns), - SetBoolAttribute { name, value, id } => { - i.SetBoolAttribute(id.0 as u32, name, value) - } + } => match value { + dioxus_core::AttributeValue::Text(txt) => { + i.SetAttribute(id.0 as u32, name, txt.into(), ns) + } + dioxus_core::AttributeValue::Float(f) => { + i.SetAttribute(id.0 as u32, name, f.into(), ns) + } + dioxus_core::AttributeValue::Int(n) => { + i.SetAttribute(id.0 as u32, name, n.into(), ns) + } + dioxus_core::AttributeValue::Bool(b) => { + i.SetBoolAttribute(id.0 as u32, name, b) + } + _ => unreachable!(), + }, SetText { value, id } => i.SetText(id.0 as u32, value.into()), NewEventListener { name, id, .. } => { self.interpreter.NewEventListener( diff --git a/packages/web/src/lib.rs b/packages/web/src/lib.rs index 62a2bf4dd..cf206b698 100644 --- a/packages/web/src/lib.rs +++ b/packages/web/src/lib.rs @@ -187,10 +187,12 @@ pub async fn run_with_props(root: fn(Scope) -> Element, root_prop // if should_hydrate { // } else { - let edits = dom.rebuild(); + { + let edits = dom.rebuild(); - websys_dom.load_templates(&edits.templates); - websys_dom.apply_edits(edits.edits); + websys_dom.load_templates(&edits.templates); + websys_dom.apply_edits(edits.edits); + } // the mutations come back with nothing - we need to actually mount them websys_dom.mount(); From da64b0c2a825718d4e0d16d3c4023b7161ee53b3 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Sat, 10 Dec 2022 12:29:15 -0600 Subject: [PATCH 003/432] WIP parising --- packages/core/src/create.rs | 19 +- packages/core/src/virtual_dom.rs | 20 + packages/rsx/src/attributes.rs | 892 ------------------------------- packages/rsx/src/lib.rs | 224 +++++++- 4 files changed, 242 insertions(+), 913 deletions(-) delete mode 100644 packages/rsx/src/attributes.rs diff --git a/packages/core/src/create.rs b/packages/core/src/create.rs index cc63a0e8b..0f23151e6 100644 --- a/packages/core/src/create.rs +++ b/packages/core/src/create.rs @@ -208,19 +208,12 @@ impl<'b> VirtualDom { // If it's all dynamic nodes, then we don't need to register it // Quickly run through and see if it's all just dynamic nodes - let dynamic_roots = template - .template - .roots - .iter() - .filter(|root| { - matches!( - root, - TemplateNode::Dynamic { .. } | TemplateNode::DynamicText { .. } - ) - }) - .count(); - - if dynamic_roots == template.template.roots.len() { + if template.template.roots.iter().all(|root| { + matches!( + root, + TemplateNode::Dynamic { .. } | TemplateNode::DynamicText { .. } + ) + }) { return; } diff --git a/packages/core/src/virtual_dom.rs b/packages/core/src/virtual_dom.rs index ced92acb0..01f11efef 100644 --- a/packages/core/src/virtual_dom.rs +++ b/packages/core/src/virtual_dom.rs @@ -458,6 +458,26 @@ impl VirtualDom { } } + /// Replace a template at runtime. This will re-render all components that use this template. + /// This is the primitive that enables hot-reloading. + /// + /// The caller must ensure that the template refrences the same dynamic attributes and nodes as the original template. + pub fn replace_template(&mut self, template: Template<'static>) { + self.templates.insert(template.name, template); + // iterating a slab is very inefficient, but this is a rare operation that will only happen during development so it's fine + for (_, scope) in &self.scopes { + if let Some(RenderReturn::Sync(Ok(sync))) = scope.try_root_node() { + if sync.template.name == template.name { + let height = scope.height; + self.dirty_scopes.insert(DirtyScope { + height, + id: scope.id, + }); + } + } + } + } + /// Performs a *full* rebuild of the virtual dom, returning every edit required to generate the actual dom from scratch. /// /// The mutations item expects the RealDom's stack to be the root of the application. diff --git a/packages/rsx/src/attributes.rs b/packages/rsx/src/attributes.rs deleted file mode 100644 index 8920d69af..000000000 --- a/packages/rsx/src/attributes.rs +++ /dev/null @@ -1,892 +0,0 @@ -use crate::elements::*; - -// map the rsx name of the attribute to the html name of the attribute, the namespace that contains it, and if the attribute is volitile -pub fn attrbute_to_static_str( - attr: &str, - element: &'static str, - namespace: Option<&'static str>, -) -> Option<(&'static str, Option<&'static str>, bool)> { - if namespace == Some("http://www.w3.org/2000/svg") { - svg::MAPPED_ATTRIBUTES - .iter() - .find(|(a, _)| *a == attr) - .map(|(_, b)| (*b, None, false)) - } else { - NO_NAMESPACE_ATTRIBUTES - .iter() - .find(|&a| *a == attr) - .map(|a| (*a, None, false)) - .or_else(|| { - STYLE_ATTRIBUTES - .iter() - .find(|(a, _)| *a == attr) - .map(|(_, b)| (*b, Some("style"), false)) - }) - .or_else(|| { - MAPPED_ATTRIBUTES - .iter() - .find(|(a, _)| *a == attr) - .map(|(_, b)| (*b, None, false)) - }) - } - .or_else(|| { - ELEMENTS_WITH_MAPPED_ATTRIBUTES - .iter() - .find_map(|(el, attrs)| { - (element == *el) - .then(|| { - attrs - .iter() - .find(|(a, _, _)| *a == attr) - .map(|(_, b, volitile)| (*b, None, *volitile)) - }) - .flatten() - }) - }) - .or_else(|| { - ELEMENTS_WITH_NAMESPACE.iter().find_map(|(el, ns, attrs)| { - (element == *el && namespace == Some(*ns)) - .then(|| { - attrs - .iter() - .find(|a| **a == attr) - .map(|a| (*a, None, false)) - }) - .flatten() - }) - }) - .or_else(|| { - ELEMENTS_WITHOUT_NAMESPACE.iter().find_map(|(el, attrs)| { - (element == *el) - .then(|| { - attrs - .iter() - .find(|a| **a == attr) - .map(|a| (*a, None, false)) - }) - .flatten() - }) - }) -} - -macro_rules! no_namespace_trait_methods { - ( - $( - $(#[$attr:meta])* - $name:ident; - )* - ) => { - pub const NO_NAMESPACE_ATTRIBUTES: &'static [&'static str] = &[ - $( - stringify!($name), - )* - ]; - }; -} - -macro_rules! style_trait_methods { - ( - $( - $(#[$attr:meta])* - $name:ident: $lit:literal, - )* - ) => { - pub const STYLE_ATTRIBUTES: &'static [(&'static str, &'static str)] = &[ - $( - (stringify!($name), $lit), - )* - ]; - }; -} - -macro_rules! mapped_trait_methods { - ( - $( - $(#[$attr:meta])* - $name:ident: $lit:literal, - )* - ) => { - pub const MAPPED_ATTRIBUTES: &'static [(&'static str, &'static str)] = &[ - $( - (stringify!($name), $lit), - )* - ("prevent_default", "dioxus-prevent-default"), - ]; - }; -} - -no_namespace_trait_methods! { - accesskey; - - /// The HTML class attribute is used to specify a class for an HTML element. - /// - /// ## Details - /// Multiple HTML elements can share the same class. - /// - /// The class global attribute is a space-separated list of the case-sensitive classes of the element. - /// Classes allow CSS and Javascript to select and access specific elements via the class selectors or - /// functions like the DOM method document.getElementsByClassName. - /// - /// ## Example - /// - /// ### HTML: - /// ```html - ///

Above point sounds a bit obvious. Remove/rewrite?

- /// ``` - /// - /// ### CSS: - /// ```css - /// .note { - /// font-style: italic; - /// font-weight: bold; - /// } - /// - /// .editorial { - /// background: rgb(255, 0, 0, .25); - /// padding: 10px; - /// } - /// ``` - class; - contenteditable; - data; - dir; - draggable; - hidden; - id; - lang; - spellcheck; - style; - tabindex; - title; - translate; - - role; - - /// dangerous_inner_html is Dioxus's replacement for using innerHTML in the browser DOM. In general, setting - /// HTML from code is risky because it’s easy to inadvertently expose your users to a cross-site scripting (XSS) - /// attack. So, you can set HTML directly from Dioxus, but you have to type out dangerous_inner_html to remind - /// yourself that it’s dangerous - dangerous_inner_html; -} - -// This macro creates an explicit method call for each of the style attributes. -// -// The left token specifies the name of the attribute in the rsx! macro, and the right string literal specifies the -// actual name of the attribute generated. -// -// This roughly follows the html spec -style_trait_methods! { - align_content: "align-content", - align_items: "align-items", - align_self: "align-self", - alignment_adjust: "alignment-adjust", - alignment_baseline: "alignment-baseline", - all: "all", - alt: "alt", - animation: "animation", - animation_delay: "animation-delay", - animation_direction: "animation-direction", - animation_duration: "animation-duration", - animation_fill_mode: "animation-fill-mode", - animation_iteration_count: "animation-iteration-count", - animation_name: "animation-name", - animation_play_state: "animation-play-state", - animation_timing_function: "animation-timing-function", - azimuth: "azimuth", - backface_visibility: "backface-visibility", - background: "background", - background_attachment: "background-attachment", - background_clip: "background-clip", - background_color: "background-color", - background_image: "background-image", - background_origin: "background-origin", - background_position: "background-position", - background_repeat: "background-repeat", - background_size: "background-size", - background_blend_mode: "background-blend-mode", - baseline_shift: "baseline-shift", - bleed: "bleed", - bookmark_label: "bookmark-label", - bookmark_level: "bookmark-level", - bookmark_state: "bookmark-state", - border: "border", - border_color: "border-color", - border_style: "border-style", - border_width: "border-width", - border_bottom: "border-bottom", - border_bottom_color: "border-bottom-color", - border_bottom_style: "border-bottom-style", - border_bottom_width: "border-bottom-width", - border_left: "border-left", - border_left_color: "border-left-color", - border_left_style: "border-left-style", - border_left_width: "border-left-width", - border_right: "border-right", - border_right_color: "border-right-color", - border_right_style: "border-right-style", - border_right_width: "border-right-width", - border_top: "border-top", - border_top_color: "border-top-color", - border_top_style: "border-top-style", - border_top_width: "border-top-width", - border_collapse: "border-collapse", - border_image: "border-image", - border_image_outset: "border-image-outset", - border_image_repeat: "border-image-repeat", - border_image_slice: "border-image-slice", - border_image_source: "border-image-source", - border_image_width: "border-image-width", - border_radius: "border-radius", - border_bottom_left_radius: "border-bottom-left-radius", - border_bottom_right_radius: "border-bottom-right-radius", - border_top_left_radius: "border-top-left-radius", - border_top_right_radius: "border-top-right-radius", - border_spacing: "border-spacing", - bottom: "bottom", - box_decoration_break: "box-decoration-break", - box_shadow: "box-shadow", - box_sizing: "box-sizing", - box_snap: "box-snap", - break_after: "break-after", - break_before: "break-before", - break_inside: "break-inside", - buffered_rendering: "buffered-rendering", - caption_side: "caption-side", - clear: "clear", - clear_side: "clear-side", - clip: "clip", - clip_path: "clip-path", - clip_rule: "clip-rule", - color: "color", - color_adjust: "color-adjust", - color_correction: "color-correction", - color_interpolation: "color-interpolation", - color_interpolation_filters: "color-interpolation-filters", - color_profile: "color-profile", - color_rendering: "color-rendering", - column_fill: "column-fill", - column_gap: "column-gap", - column_rule: "column-rule", - column_rule_color: "column-rule-color", - column_rule_style: "column-rule-style", - column_rule_width: "column-rule-width", - column_span: "column-span", - columns: "columns", - column_count: "column-count", - column_width: "column-width", - contain: "contain", - content: "content", - counter_increment: "counter-increment", - counter_reset: "counter-reset", - counter_set: "counter-set", - cue: "cue", - cue_after: "cue-after", - cue_before: "cue-before", - cursor: "cursor", - direction: "direction", - display: "display", - display_inside: "display-inside", - display_outside: "display-outside", - display_extras: "display-extras", - display_box: "display-box", - dominant_baseline: "dominant-baseline", - elevation: "elevation", - empty_cells: "empty-cells", - enable_background: "enable-background", - fill: "fill", - fill_opacity: "fill-opacity", - fill_rule: "fill-rule", - filter: "filter", - float: "float", - float_defer_column: "float-defer-column", - float_defer_page: "float-defer-page", - float_offset: "float-offset", - float_wrap: "float-wrap", - flow_into: "flow-into", - flow_from: "flow-from", - flex: "flex", - flex_basis: "flex-basis", - flex_grow: "flex-grow", - flex_shrink: "flex-shrink", - flex_flow: "flex-flow", - flex_direction: "flex-direction", - flex_wrap: "flex-wrap", - flood_color: "flood-color", - flood_opacity: "flood-opacity", - font: "font", - font_family: "font-family", - font_size: "font-size", - font_stretch: "font-stretch", - font_style: "font-style", - font_weight: "font-weight", - font_feature_settings: "font-feature-settings", - font_kerning: "font-kerning", - font_language_override: "font-language-override", - font_size_adjust: "font-size-adjust", - font_synthesis: "font-synthesis", - font_variant: "font-variant", - font_variant_alternates: "font-variant-alternates", - font_variant_caps: "font-variant-caps", - font_variant_east_asian: "font-variant-east-asian", - font_variant_ligatures: "font-variant-ligatures", - font_variant_numeric: "font-variant-numeric", - font_variant_position: "font-variant-position", - footnote_policy: "footnote-policy", - glyph_orientation_horizontal: "glyph-orientation-horizontal", - glyph_orientation_vertical: "glyph-orientation-vertical", - grid: "grid", - grid_auto_flow: "grid-auto-flow", - grid_auto_columns: "grid-auto-columns", - grid_auto_rows: "grid-auto-rows", - grid_template: "grid-template", - grid_template_areas: "grid-template-areas", - grid_template_columns: "grid-template-columns", - grid_template_rows: "grid-template-rows", - grid_area: "grid-area", - grid_column: "grid-column", - grid_column_start: "grid-column-start", - grid_column_end: "grid-column-end", - grid_row: "grid-row", - grid_row_start: "grid-row-start", - grid_row_end: "grid-row-end", - hanging_punctuation: "hanging-punctuation", - height: "height", - hyphenate_character: "hyphenate-character", - hyphenate_limit_chars: "hyphenate-limit-chars", - hyphenate_limit_last: "hyphenate-limit-last", - hyphenate_limit_lines: "hyphenate-limit-lines", - hyphenate_limit_zone: "hyphenate-limit-zone", - hyphens: "hyphens", - icon: "icon", - image_orientation: "image-orientation", - image_resolution: "image-resolution", - image_rendering: "image-rendering", - ime: "ime", - ime_align: "ime-align", - ime_mode: "ime-mode", - ime_offset: "ime-offset", - ime_width: "ime-width", - initial_letters: "initial-letters", - inline_box_align: "inline-box-align", - isolation: "isolation", - justify_content: "justify-content", - justify_items: "justify-items", - justify_self: "justify-self", - kerning: "kerning", - left: "left", - letter_spacing: "letter-spacing", - lighting_color: "lighting-color", - line_box_contain: "line-box-contain", - line_break: "line-break", - line_grid: "line-grid", - line_height: "line-height", - line_slack: "line-slack", - line_snap: "line-snap", - list_style: "list-style", - list_style_image: "list-style-image", - list_style_position: "list-style-position", - list_style_type: "list-style-type", - margin: "margin", - margin_bottom: "margin-bottom", - margin_left: "margin-left", - margin_right: "margin-right", - margin_top: "margin-top", - marker: "marker", - marker_end: "marker-end", - marker_mid: "marker-mid", - marker_pattern: "marker-pattern", - marker_segment: "marker-segment", - marker_start: "marker-start", - marker_knockout_left: "marker-knockout-left", - marker_knockout_right: "marker-knockout-right", - marker_side: "marker-side", - marks: "marks", - marquee_direction: "marquee-direction", - marquee_play_count: "marquee-play-count", - marquee_speed: "marquee-speed", - marquee_style: "marquee-style", - mask: "mask", - mask_image: "mask-image", - mask_repeat: "mask-repeat", - mask_position: "mask-position", - mask_clip: "mask-clip", - mask_origin: "mask-origin", - mask_size: "mask-size", - mask_box: "mask-box", - mask_box_outset: "mask-box-outset", - mask_box_repeat: "mask-box-repeat", - mask_box_slice: "mask-box-slice", - mask_box_source: "mask-box-source", - mask_box_width: "mask-box-width", - mask_type: "mask-type", - max_height: "max-height", - max_lines: "max-lines", - max_width: "max-width", - min_height: "min-height", - min_width: "min-width", - mix_blend_mode: "mix-blend-mode", - nav_down: "nav-down", - nav_index: "nav-index", - nav_left: "nav-left", - nav_right: "nav-right", - nav_up: "nav-up", - object_fit: "object-fit", - object_position: "object-position", - offset_after: "offset-after", - offset_before: "offset-before", - offset_end: "offset-end", - offset_start: "offset-start", - opacity: "opacity", - order: "order", - orphans: "orphans", - outline: "outline", - outline_color: "outline-color", - outline_style: "outline-style", - outline_width: "outline-width", - outline_offset: "outline-offset", - overflow: "overflow", - overflow_x: "overflow-x", - overflow_y: "overflow-y", - overflow_style: "overflow-style", - overflow_wrap: "overflow-wrap", - padding: "padding", - padding_bottom: "padding-bottom", - padding_left: "padding-left", - padding_right: "padding-right", - padding_top: "padding-top", - page: "page", - page_break_after: "page-break-after", - page_break_before: "page-break-before", - page_break_inside: "page-break-inside", - paint_order: "paint-order", - pause: "pause", - pause_after: "pause-after", - pause_before: "pause-before", - perspective: "perspective", - perspective_origin: "perspective-origin", - pitch: "pitch", - pitch_range: "pitch-range", - play_during: "play-during", - pointer_events: "pointer-events", - position: "position", - quotes: "quotes", - region_fragment: "region-fragment", - resize: "resize", - rest: "rest", - rest_after: "rest-after", - rest_before: "rest-before", - richness: "richness", - right: "right", - ruby_align: "ruby-align", - ruby_merge: "ruby-merge", - ruby_position: "ruby-position", - scroll_behavior: "scroll-behavior", - scroll_snap_coordinate: "scroll-snap-coordinate", - scroll_snap_destination: "scroll-snap-destination", - scroll_snap_points_x: "scroll-snap-points-x", - scroll_snap_points_y: "scroll-snap-points-y", - scroll_snap_type: "scroll-snap-type", - shape_image_threshold: "shape-image-threshold", - shape_inside: "shape-inside", - shape_margin: "shape-margin", - shape_outside: "shape-outside", - shape_padding: "shape-padding", - shape_rendering: "shape-rendering", - size: "size", - speak: "speak", - speak_as: "speak-as", - speak_header: "speak-header", - speak_numeral: "speak-numeral", - speak_punctuation: "speak-punctuation", - speech_rate: "speech-rate", - stop_color: "stop-color", - stop_opacity: "stop-opacity", - stress: "stress", - string_set: "string-set", - stroke: "stroke", - stroke_dasharray: "stroke-dasharray", - stroke_dashoffset: "stroke-dashoffset", - stroke_linecap: "stroke-linecap", - stroke_linejoin: "stroke-linejoin", - stroke_miterlimit: "stroke-miterlimit", - stroke_opacity: "stroke-opacity", - stroke_width: "stroke-width", - tab_size: "tab-size", - table_layout: "table-layout", - text_align: "text-align", - text_align_all: "text-align-all", - text_align_last: "text-align-last", - text_anchor: "text-anchor", - text_combine_upright: "text-combine-upright", - text_decoration: "text-decoration", - text_decoration_color: "text-decoration-color", - text_decoration_line: "text-decoration-line", - text_decoration_style: "text-decoration-style", - text_decoration_skip: "text-decoration-skip", - text_emphasis: "text-emphasis", - text_emphasis_color: "text-emphasis-color", - text_emphasis_style: "text-emphasis-style", - text_emphasis_position: "text-emphasis-position", - text_emphasis_skip: "text-emphasis-skip", - text_height: "text-height", - text_indent: "text-indent", - text_justify: "text-justify", - text_orientation: "text-orientation", - text_overflow: "text-overflow", - text_rendering: "text-rendering", - text_shadow: "text-shadow", - text_size_adjust: "text-size-adjust", - text_space_collapse: "text-space-collapse", - text_spacing: "text-spacing", - text_transform: "text-transform", - text_underline_position: "text-underline-position", - text_wrap: "text-wrap", - top: "top", - touch_action: "touch-action", - transform: "transform", - transform_box: "transform-box", - transform_origin: "transform-origin", - transform_style: "transform-style", - transition: "transition", - transition_delay: "transition-delay", - transition_duration: "transition-duration", - transition_property: "transition-property", - unicode_bidi: "unicode-bidi", - vector_effect: "vector-effect", - vertical_align: "vertical-align", - visibility: "visibility", - voice_balance: "voice-balance", - voice_duration: "voice-duration", - voice_family: "voice-family", - voice_pitch: "voice-pitch", - voice_range: "voice-range", - voice_rate: "voice-rate", - voice_stress: "voice-stress", - voice_volumn: "voice-volumn", - volume: "volume", - white_space: "white-space", - widows: "widows", - width: "width", - will_change: "will-change", - word_break: "word-break", - word_spacing: "word-spacing", - word_wrap: "word-wrap", - wrap_flow: "wrap-flow", - wrap_through: "wrap-through", - writing_mode: "writing-mode", - gap: "gap", - list_styler_type: "list-style-type", - row_gap: "row-gap", - transition_timing_function: "transition-timing-function", - user_select: "user-select", - webkit_user_select: "-webkit-user-select", - z_index : "z-index", -} -mapped_trait_methods! { - aria_current: "aria-current", - aria_details: "aria-details", - aria_disabled: "aria-disabled", - aria_hidden: "aria-hidden", - aria_invalid: "aria-invalid", - aria_keyshortcuts: "aria-keyshortcuts", - aria_label: "aria-label", - aria_roledescription: "aria-roledescription", - // Widget Attributes - aria_autocomplete: "aria-autocomplete", - aria_checked: "aria-checked", - aria_expanded: "aria-expanded", - aria_haspopup: "aria-haspopup", - aria_level: "aria-level", - aria_modal: "aria-modal", - aria_multiline: "aria-multiline", - aria_multiselectable: "aria-multiselectable", - aria_orientation: "aria-orientation", - aria_placeholder: "aria-placeholder", - aria_pressed: "aria-pressed", - aria_readonly: "aria-readonly", - aria_required: "aria-required", - aria_selected: "aria-selected", - aria_sort: "aria-sort", - aria_valuemax: "aria-valuemax", - aria_valuemin: "aria-valuemin", - aria_valuenow: "aria-valuenow", - aria_valuetext: "aria-valuetext", - // Live Region Attributes - aria_atomic: "aria-atomic", - aria_busy: "aria-busy", - aria_live: "aria-live", - aria_relevant: "aria-relevant", - - aria_dropeffect: "aria-dropeffect", - aria_grabbed: "aria-grabbed", - // Relationship Attributes - aria_activedescendant: "aria-activedescendant", - aria_colcount: "aria-colcount", - aria_colindex: "aria-colindex", - aria_colspan: "aria-colspan", - aria_controls: "aria-controls", - aria_describedby: "aria-describedby", - aria_errormessage: "aria-errormessage", - aria_flowto: "aria-flowto", - aria_labelledby: "aria-labelledby", - aria_owns: "aria-owns", - aria_posinset: "aria-posinset", - aria_rowcount: "aria-rowcount", - aria_rowindex: "aria-rowindex", - aria_rowspan: "aria-rowspan", - aria_setsize: "aria-setsize", -} - -pub mod svg { - mapped_trait_methods! { - accent_height: "accent-height", - accumulate: "accumulate", - additive: "additive", - alignment_baseline: "alignment-baseline", - alphabetic: "alphabetic", - amplitude: "amplitude", - arabic_form: "arabic-form", - ascent: "ascent", - attributeName: "attributeName", - attributeType: "attributeType", - azimuth: "azimuth", - baseFrequency: "baseFrequency", - baseline_shift: "baseline-shift", - baseProfile: "baseProfile", - bbox: "bbox", - begin: "begin", - bias: "bias", - by: "by", - calcMode: "calcMode", - cap_height: "cap-height", - class: "class", - clip: "clip", - clipPathUnits: "clipPathUnits", - clip_path: "clip-path", - clip_rule: "clip-rule", - color: "color", - color_interpolation: "color-interpolation", - color_interpolation_filters: "color-interpolation-filters", - color_profile: "color-profile", - color_rendering: "color-rendering", - contentScriptType: "contentScriptType", - contentStyleType: "contentStyleType", - crossorigin: "crossorigin", - cursor: "cursor", - cx: "cx", - cy: "cy", - d: "d", - decelerate: "decelerate", - descent: "descent", - diffuseConstant: "diffuseConstant", - direction: "direction", - display: "display", - divisor: "divisor", - dominant_baseline: "dominant-baseline", - dur: "dur", - dx: "dx", - dy: "dy", - edgeMode: "edgeMode", - elevation: "elevation", - enable_background: "enable-background", - end: "end", - exponent: "exponent", - fill: "fill", - fill_opacity: "fill-opacity", - fill_rule: "fill-rule", - filter: "filter", - filterRes: "filterRes", - filterUnits: "filterUnits", - flood_color: "flood-color", - flood_opacity: "flood-opacity", - font_family: "font-family", - font_size: "font-size", - font_size_adjust: "font-size-adjust", - font_stretch: "font-stretch", - font_style: "font-style", - font_variant: "font-variant", - font_weight: "font-weight", - format: "format", - from: "from", - fr: "fr", - fx: "fx", - fy: "fy", - g1: "g1", - g2: "g2", - glyph_name: "glyph-name", - glyph_orientation_horizontal: "glyph-orientation-horizontal", - glyph_orientation_vertical: "glyph-orientation-vertical", - glyphRef: "glyphRef", - gradientTransform: "gradientTransform", - gradientUnits: "gradientUnits", - hanging: "hanging", - height: "height", - href: "href", - hreflang: "hreflang", - horiz_adv_x: "horiz-adv-x", - horiz_origin_x: "horiz-origin-x", - id: "id", - ideographic: "ideographic", - image_rendering: "image-rendering", - _in: "_in", - in2: "in2", - intercept: "intercept", - k: "k", - k1: "k1", - k2: "k2", - k3: "k3", - k4: "k4", - kernelMatrix: "kernelMatrix", - kernelUnitLength: "kernelUnitLength", - kerning: "kerning", - keyPoints: "keyPoints", - keySplines: "keySplines", - keyTimes: "keyTimes", - lang: "lang", - lengthAdjust: "lengthAdjust", - letter_spacing: "letter-spacing", - lighting_color: "lighting-color", - limitingConeAngle: "limitingConeAngle", - local: "local", - marker_end: "marker-end", - marker_mid: "marker-mid", - marker_start: "marker_start", - markerHeight: "markerHeight", - markerUnits: "markerUnits", - markerWidth: "markerWidth", - mask: "mask", - maskContentUnits: "maskContentUnits", - maskUnits: "maskUnits", - mathematical: "mathematical", - max: "max", - media: "media", - method: "method", - min: "min", - mode: "mode", - name: "name", - numOctaves: "numOctaves", - offset: "offset", - opacity: "opacity", - operator: "operator", - order: "order", - orient: "orient", - orientation: "orientation", - origin: "origin", - overflow: "overflow", - overline_position: "overline-position", - overline_thickness: "overline-thickness", - panose_1: "panose-1", - paint_order: "paint-order", - path: "path", - pathLength: "pathLength", - patternContentUnits: "patternContentUnits", - patternTransform: "patternTransform", - patternUnits: "patternUnits", - ping: "ping", - pointer_events: "pointer-events", - points: "points", - pointsAtX: "pointsAtX", - pointsAtY: "pointsAtY", - pointsAtZ: "pointsAtZ", - preserveAlpha: "preserveAlpha", - preserveAspectRatio: "preserveAspectRatio", - primitiveUnits: "primitiveUnits", - r: "r", - radius: "radius", - referrerPolicy: "referrerPolicy", - refX: "refX", - refY: "refY", - rel: "rel", - rendering_intent: "rendering-intent", - repeatCount: "repeatCount", - repeatDur: "repeatDur", - requiredExtensions: "requiredExtensions", - requiredFeatures: "requiredFeatures", - restart: "restart", - result: "result", - role: "role", - rotate: "rotate", - rx: "rx", - ry: "ry", - scale: "scale", - seed: "seed", - shape_rendering: "shape-rendering", - slope: "slope", - spacing: "spacing", - specularConstant: "specularConstant", - specularExponent: "specularExponent", - speed: "speed", - spreadMethod: "spreadMethod", - startOffset: "startOffset", - stdDeviation: "stdDeviation", - stemh: "stemh", - stemv: "stemv", - stitchTiles: "stitchTiles", - stop_color: "stop_color", - stop_opacity: "stop_opacity", - strikethrough_position: "strikethrough-position", - strikethrough_thickness: "strikethrough-thickness", - string: "string", - stroke: "stroke", - stroke_dasharray: "stroke-dasharray", - stroke_dashoffset: "stroke-dashoffset", - stroke_linecap: "stroke-linecap", - stroke_linejoin: "stroke-linejoin", - stroke_miterlimit: "stroke-miterlimit", - stroke_opacity: "stroke-opacity", - stroke_width: "stroke-width", - style: "style", - surfaceScale: "surfaceScale", - systemLanguage: "systemLanguage", - tabindex: "tabindex", - tableValues: "tableValues", - target: "target", - targetX: "targetX", - targetY: "targetY", - text_anchor: "text-anchor", - text_decoration: "text-decoration", - text_rendering: "text-rendering", - textLength: "textLength", - to: "to", - transform: "transform", - transform_origin: "transform-origin", - r#type: "_type", - u1: "u1", - u2: "u2", - underline_position: "underline-position", - underline_thickness: "underline-thickness", - unicode: "unicode", - unicode_bidi: "unicode-bidi", - unicode_range: "unicode-range", - units_per_em: "units-per-em", - v_alphabetic: "v-alphabetic", - v_hanging: "v-hanging", - v_ideographic: "v-ideographic", - v_mathematical: "v-mathematical", - values: "values", - vector_effect: "vector-effect", - version: "version", - vert_adv_y: "vert-adv-y", - vert_origin_x: "vert-origin-x", - vert_origin_y: "vert-origin-y", - view_box: "viewBox", - view_target: "viewTarget", - visibility: "visibility", - width: "width", - widths: "widths", - word_spacing: "word-spacing", - writing_mode: "writing-mode", - x: "x", - x_height: "x-height", - x1: "x1", - x2: "x2", - xmlns: "xmlns", - x_channel_selector: "xChannelSelector", - y: "y", - y1: "y1", - y2: "y2", - y_channel_selector: "yChannelSelector", - z: "z", - zoomAndPan: "zoomAndPan", - } -} diff --git a/packages/rsx/src/lib.rs b/packages/rsx/src/lib.rs index ccfcda8fa..65d48f18a 100644 --- a/packages/rsx/src/lib.rs +++ b/packages/rsx/src/lib.rs @@ -17,10 +17,10 @@ mod component; mod element; mod ifmt; mod node; -mod template; // Re-export the namespaces into each other pub use component::*; +use dioxus_core::{Template, TemplateAttribute, TemplateNode}; pub use element::*; pub use ifmt::*; pub use node::*; @@ -42,6 +42,16 @@ pub struct CallBody { pub inline_cx: bool, } +impl CallBody { + /// This function intentionally leaks memory to create a static template. + /// Keeping the template static allows us to simplify the core of dioxus and leaking memory in dev mode is less of an issue. + /// the previous_location is the location of the previous template at the time the template was originally compiled. + pub fn leak_template(&self, previous_location: &'static str) -> Template { + let mut renderer = TemplateRenderer { roots: &self.roots }; + renderer.leak_template(previous_location) + } +} + impl Parse for CallBody { fn parse(input: ParseStream) -> Result { let mut roots = Vec::new(); @@ -89,15 +99,48 @@ pub struct TemplateRenderer<'a> { pub roots: &'a [BodyNode], } +impl<'a> TemplateRenderer<'a> { + fn leak_template(&mut self, previous_location: &'static str) -> Template<'static> { + let mut context = DynamicContext::default(); + + let roots: Vec<_> = self + .roots + .iter() + .enumerate() + .map(|(idx, root)| { + context.current_path.push(idx as u8); + let out = context.leak_node(root); + context.current_path.pop(); + out + }) + .collect(); + + Template { + name: previous_location, + roots: Box::leak(roots.into_boxed_slice()), + node_paths: Box::leak( + context + .node_paths + .into_iter() + .map(|path| &*Box::leak(path.into_boxed_slice())) + .collect::>() + .into_boxed_slice(), + ), + attr_paths: Box::leak( + context + .attr_paths + .into_iter() + .map(|path| &*Box::leak(path.into_boxed_slice())) + .collect::>() + .into_boxed_slice(), + ), + } + } +} + impl<'a> ToTokens for TemplateRenderer<'a> { fn to_tokens(&self, out_tokens: &mut TokenStream2) { - let mut context = DynamicContext { - dynamic_nodes: vec![], - dynamic_attributes: vec![], - current_path: vec![], - attr_paths: vec![], - node_paths: vec![], - }; + let mut context = DynamicContext::default(); let key = match self.roots.get(0) { Some(BodyNode::Element(el)) if self.roots.len() == 1 => el.key.clone(), @@ -156,6 +199,7 @@ impl<'a> ToTokens for TemplateRenderer<'a> { } // As we print out the dynamic nodes, we want to keep track of them in a linear fashion // We'll use the size of the vecs to determine the index of the dynamic node in the final +#[derive(Default)] pub struct DynamicContext<'a> { dynamic_nodes: Vec<&'a BodyNode>, dynamic_attributes: Vec<&'a ElementAttrNamed>, @@ -166,6 +210,111 @@ pub struct DynamicContext<'a> { } impl<'a> DynamicContext<'a> { + fn leak_node(&mut self, root: &'a BodyNode) -> TemplateNode<'static> { + match root { + BodyNode::Element(el) => { + // dynamic attributes + // [0] + // [0, 1] + // [0, 1] + // [0, 1] + // [0, 1, 2] + // [0, 2] + // [0, 2, 1] + + let static_attrs: Vec<_> = el + .attributes + .iter() + .map(|attr| match &attr.attr { + ElementAttr::AttrText { name: _, value } if value.is_static() => { + let value = value.source.as_ref().unwrap(); + TemplateAttribute::Static { + name: "todo", + namespace: None, + value: Box::leak(value.value().into_boxed_str()), + // name: dioxus_elements::#el_name::#name.0, + // namespace: dioxus_elements::#el_name::#name.1, + // value: #value, + + // todo: we don't diff these so we never apply the volatile flag + // volatile: dioxus_elements::#el_name::#name.2, + } + } + + ElementAttr::CustomAttrText { name, value } if value.is_static() => { + let value = value.source.as_ref().unwrap(); + TemplateAttribute::Static { + name: Box::leak(name.value().into_boxed_str()), + namespace: None, + value: Box::leak(value.value().into_boxed_str()), + // todo: we don't diff these so we never apply the volatile flag + // volatile: dioxus_elements::#el_name::#name.2, + } + } + + ElementAttr::AttrExpression { .. } + | ElementAttr::AttrText { .. } + | ElementAttr::CustomAttrText { .. } + | ElementAttr::CustomAttrExpression { .. } + | ElementAttr::EventTokens { .. } => { + let ct = self.dynamic_attributes.len(); + self.dynamic_attributes.push(attr); + self.attr_paths.push(self.current_path.clone()); + TemplateAttribute::Dynamic { id: ct } + } + }) + .collect(); + + let children: Vec<_> = el + .children + .iter() + .enumerate() + .map(|(idx, root)| { + self.current_path.push(idx as u8); + let out = self.leak_node(root); + self.current_path.pop(); + out + }) + .collect(); + + // TemplateNode::Element { + // tag: dioxus_elements::#el_name::TAG_NAME, + // namespace: dioxus_elements::#el_name::NAME_SPACE, + // attrs: &[ #attrs ], + // children: &[ #children ], + // } + TemplateNode::Element { + tag: "todo", + namespace: None, + attrs: Box::leak(static_attrs.into_boxed_slice()), + children: Box::leak(children.into_boxed_slice()), + } + } + + BodyNode::Text(text) if text.is_static() => { + let text = text.source.as_ref().unwrap(); + TemplateNode::Text { + text: Box::leak(text.value().into_boxed_str()), + } + } + + BodyNode::RawExpr(_) + | BodyNode::Text(_) + | BodyNode::ForLoop(_) + | BodyNode::IfChain(_) + | BodyNode::Component(_) => { + let ct = self.dynamic_nodes.len(); + self.dynamic_nodes.push(root); + self.node_paths.push(self.current_path.clone()); + + match root { + BodyNode::Text(_) => TemplateNode::DynamicText { id: ct }, + _ => TemplateNode::Dynamic { id: ct }, + } + } + } + } + fn render_static_node(&mut self, root: &'a BodyNode) -> TokenStream2 { match root { BodyNode::Element(el) => { @@ -267,3 +416,62 @@ impl<'a> DynamicContext<'a> { } } } + +#[test] +fn template() { + let input = quote! { + div { + width: 100, + height: "100px", + "width2": 100, + "height2": "100px", + p { + "hello world" + } + (0..10).map(|i| rsx!{"{i}"}) + } + }; + let call_body: CallBody = syn::parse2(input).unwrap(); + + let template = call_body.leak_template("testing"); + + dbg!(template); + + assert_eq!( + template, + Template { + name: "testing", + roots: &[TemplateNode::Element { + tag: "div", + namespace: None, + attrs: &[ + TemplateAttribute::Dynamic { id: 0 }, + TemplateAttribute::Static { + name: "height", + namespace: None, + value: "100px", + }, + TemplateAttribute::Dynamic { id: 1 }, + TemplateAttribute::Static { + name: "height2", + namespace: None, + value: "100px", + }, + ], + children: &[ + TemplateNode::Element { + tag: "p", + namespace: None, + attrs: &[], + children: &[TemplateNode::Text { + text: "hello world", + }], + }, + TemplateNode::Dynamic { id: 0 } + ], + }], + node_paths: &[&[0, 1,],], + attr_paths: &[&[0,], &[0,],], + }, + ) +} From 0813d531f157f87e479bb9a1416d57a7fa4feb3b Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Sat, 10 Dec 2022 12:50:15 -0600 Subject: [PATCH 004/432] add some utilities around AnyValueRc --- packages/core/src/lib.rs | 2 +- packages/core/src/nodes.rs | 49 ++++++++++++++++++++++++++++---- packages/native-core/src/node.rs | 4 +-- packages/tui/src/lib.rs | 4 +-- packages/tui/src/query.rs | 2 +- 5 files changed, 49 insertions(+), 12 deletions(-) diff --git a/packages/core/src/lib.rs b/packages/core/src/lib.rs index 0de74c911..fd8a934f6 100644 --- a/packages/core/src/lib.rs +++ b/packages/core/src/lib.rs @@ -70,7 +70,7 @@ pub(crate) mod innerlude { } pub use crate::innerlude::{ - fc_to_builder, AnyValueBox, Attribute, AttributeValue, Component, DynamicNode, Element, + fc_to_builder, AnyValueRc, Attribute, AttributeValue, Component, DynamicNode, Element, ElementId, Event, Fragment, IntoAttributeValue, IntoDynNode, LazyNodes, Mutation, Mutations, Properties, RenderReturn, Scope, ScopeId, ScopeState, Scoped, SuspenseContext, TaskId, Template, TemplateAttribute, TemplateNode, VComponent, VNode, VText, VirtualDom, diff --git a/packages/core/src/nodes.rs b/packages/core/src/nodes.rs index c776da967..62b88ac83 100644 --- a/packages/core/src/nodes.rs +++ b/packages/core/src/nodes.rs @@ -338,7 +338,7 @@ pub enum AttributeValue<'a> { Listener(ListenerCb<'a>), /// An arbitrary value that implements PartialEq and is static - Any(AnyValueBox), + Any(AnyValueRc), /// A "none" value, resulting in the removal of an attribute from the dom None, @@ -375,10 +375,47 @@ impl<'de, 'a> serde::Deserialize<'de> for ListenerCb<'a> { /// A boxed value that implements PartialEq and Any #[derive(Clone)] -pub struct AnyValueBox(pub Rc); +pub struct AnyValueRc(pub Rc); + +impl PartialEq for AnyValueRc { + fn eq(&self, other: &Self) -> bool { + self.0.any_cmp(other.0.as_ref()) + } +} + +impl AnyValueRc { + /// Returns a reference to the inner value without checking the type. + pub fn downcast_ref_unchecked(&self) -> &T { + unsafe { &*(self.0.as_ref() as *const dyn AnyValue as *const T) } + } + + /// Returns a reference to the inner value. + pub fn downcast_ref(&self) -> Option<&T> { + if self.0.our_typeid() == TypeId::of::() { + Some(self.downcast_ref_unchecked()) + } else { + None + } + } + + /// Checks if the inner value is of type `T`. + pub fn is(&self) -> bool { + self.0.our_typeid() == TypeId::of::() + } +} + +#[test] +fn test_any_value_rc() { + let a = AnyValueRc(Rc::new(1i32)); + assert!(a.is::()); + assert!(!a.is::()); + assert_eq!(a.downcast_ref::(), Some(&1i32)); + assert_eq!(a.downcast_ref::(), None); + assert_eq!(a.downcast_ref_unchecked::(), &1i32); +} #[cfg(feature = "serialize")] -impl serde::Serialize for AnyValueBox { +impl serde::Serialize for AnyValueRc { fn serialize(&self, _: S) -> Result where S: serde::Serializer, @@ -388,7 +425,7 @@ impl serde::Serialize for AnyValueBox { } #[cfg(feature = "serialize")] -impl<'de> serde::Deserialize<'de> for AnyValueBox { +impl<'de> serde::Deserialize<'de> for AnyValueRc { fn deserialize(_: D) -> Result where D: serde::Deserializer<'de>, @@ -419,7 +456,7 @@ impl<'a> PartialEq for AttributeValue<'a> { (Self::Int(l0), Self::Int(r0)) => l0 == r0, (Self::Bool(l0), Self::Bool(r0)) => l0 == r0, (Self::Listener(_), Self::Listener(_)) => true, - (Self::Any(l0), Self::Any(r0)) => l0.0.any_cmp(r0.0.as_ref()), + (Self::Any(l0), Self::Any(r0)) => l0 == r0, _ => false, } } @@ -642,7 +679,7 @@ impl<'a> IntoAttributeValue<'a> for Arguments<'_> { } } -impl<'a> IntoAttributeValue<'a> for AnyValueBox { +impl<'a> IntoAttributeValue<'a> for AnyValueRc { fn into_value(self, _: &'a Bump) -> AttributeValue<'a> { AttributeValue::Any(self) } diff --git a/packages/native-core/src/node.rs b/packages/native-core/src/node.rs index 7220e0fab..6ee7d7184 100644 --- a/packages/native-core/src/node.rs +++ b/packages/native-core/src/node.rs @@ -1,5 +1,5 @@ use crate::{state::State, tree::NodeId}; -use dioxus_core::{AnyValueBox, AttributeValue, ElementId}; +use dioxus_core::{AnyValueRc, AttributeValue, ElementId}; use rustc_hash::{FxHashMap, FxHashSet}; use std::fmt::Debug; @@ -79,7 +79,7 @@ pub enum OwnedAttributeValue { Float(f64), Int(i64), Bool(bool), - Any(AnyValueBox), + Any(AnyValueRc), None, } diff --git a/packages/tui/src/lib.rs b/packages/tui/src/lib.rs index 38638f7c5..e60988a24 100644 --- a/packages/tui/src/lib.rs +++ b/packages/tui/src/lib.rs @@ -185,9 +185,9 @@ fn render_vdom( let rdom = rdom.borrow(); let mut taffy = taffy.lock().expect("taffy lock poisoned"); // size is guaranteed to not change when rendering - resize(frame.size(), &mut *taffy, &rdom); + resize(frame.size(), &mut taffy, &rdom); let root = &rdom[NodeId(0)]; - render::render_vnode(frame, &*taffy, &rdom, root, cfg, Point::ZERO); + render::render_vnode(frame, &taffy, &rdom, root, cfg, Point::ZERO); })?; } else { let rdom = rdom.borrow(); diff --git a/packages/tui/src/query.rs b/packages/tui/src/query.rs index 64da87650..c052ad9ea 100644 --- a/packages/tui/src/query.rs +++ b/packages/tui/src/query.rs @@ -89,7 +89,7 @@ impl<'a> ElementRef<'a> { .ok(); layout.map(|layout| Layout { order: layout.order, - size: layout.size.map(|v| layout_to_screen_space(v)), + size: layout.size.map(layout_to_screen_space), location: Point { x: layout_to_screen_space(layout.location.x), y: layout_to_screen_space(layout.location.y), From e4a7e4582bc29f5ac038f24b43724f3fffef0fba Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Sat, 10 Dec 2022 12:59:12 -0600 Subject: [PATCH 005/432] make downcast_ref_unchecked unsafe --- packages/core/src/nodes.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/packages/core/src/nodes.rs b/packages/core/src/nodes.rs index 62b88ac83..d00825c34 100644 --- a/packages/core/src/nodes.rs +++ b/packages/core/src/nodes.rs @@ -385,14 +385,17 @@ impl PartialEq for AnyValueRc { impl AnyValueRc { /// Returns a reference to the inner value without checking the type. - pub fn downcast_ref_unchecked(&self) -> &T { + /// + /// # Safety + /// The caller must ensure that the type of the inner value is `T`. + pub unsafe fn downcast_ref_unchecked(&self) -> &T { unsafe { &*(self.0.as_ref() as *const dyn AnyValue as *const T) } } /// Returns a reference to the inner value. pub fn downcast_ref(&self) -> Option<&T> { if self.0.our_typeid() == TypeId::of::() { - Some(self.downcast_ref_unchecked()) + Some(unsafe { self.downcast_ref_unchecked() }) } else { None } @@ -411,7 +414,9 @@ fn test_any_value_rc() { assert!(!a.is::()); assert_eq!(a.downcast_ref::(), Some(&1i32)); assert_eq!(a.downcast_ref::(), None); - assert_eq!(a.downcast_ref_unchecked::(), &1i32); + unsafe { + assert_eq!(a.downcast_ref_unchecked::(), &1i32); + } } #[cfg(feature = "serialize")] From 0ac02cc89391f6067ab5e95f82dee67d0dd9ac7f Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Sat, 10 Dec 2022 13:24:39 -0600 Subject: [PATCH 006/432] move any bound to trait instead of implementation --- packages/core/src/nodes.rs | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/packages/core/src/nodes.rs b/packages/core/src/nodes.rs index d00825c34..3e8638a84 100644 --- a/packages/core/src/nodes.rs +++ b/packages/core/src/nodes.rs @@ -394,7 +394,7 @@ impl AnyValueRc { /// Returns a reference to the inner value. pub fn downcast_ref(&self) -> Option<&T> { - if self.0.our_typeid() == TypeId::of::() { + if self.0.type_id() == TypeId::of::() { Some(unsafe { self.downcast_ref_unchecked() }) } else { None @@ -403,7 +403,7 @@ impl AnyValueRc { /// Checks if the inner value is of type `T`. pub fn is(&self) -> bool { - self.0.our_typeid() == TypeId::of::() + self.0.type_id() == TypeId::of::() } } @@ -468,23 +468,18 @@ impl<'a> PartialEq for AttributeValue<'a> { } #[doc(hidden)] -pub trait AnyValue { +pub trait AnyValue: Any { fn any_cmp(&self, other: &dyn AnyValue) -> bool; - fn our_typeid(&self) -> TypeId; } impl AnyValue for T { fn any_cmp(&self, other: &dyn AnyValue) -> bool { - if self.type_id() != other.our_typeid() { + if self.type_id() != other.type_id() { return false; } self == unsafe { &*(other as *const _ as *const T) } } - - fn our_typeid(&self) -> TypeId { - self.type_id() - } } #[doc(hidden)] From 2131e5658bc4679a8429ed28ab087a199d290b8f Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Sat, 10 Dec 2022 14:09:59 -0600 Subject: [PATCH 007/432] add hot reloading context trait --- packages/rsx/src/component.rs | 5 +- packages/rsx/src/hot_reloading_context.rs | 19 ++++ packages/rsx/src/lib.rs | 117 ++++++++++++++++------ packages/rsx/src/node.rs | 7 +- 4 files changed, 116 insertions(+), 32 deletions(-) create mode 100644 packages/rsx/src/hot_reloading_context.rs diff --git a/packages/rsx/src/component.rs b/packages/rsx/src/component.rs index 2dddb1533..9d3e75f25 100644 --- a/packages/rsx/src/component.rs +++ b/packages/rsx/src/component.rs @@ -11,6 +11,8 @@ //! - [ ] Keys //! - [ ] Properties spreading with with `..` syntax +use std::marker::PhantomData; + use super::*; use proc_macro2::TokenStream as TokenStream2; @@ -165,8 +167,9 @@ impl ToTokens for Component { } if !self.children.is_empty() { - let renderer = TemplateRenderer { + let renderer: TemplateRenderer = TemplateRenderer { roots: &self.children, + phantom: PhantomData, }; toks.append_all(quote! { diff --git a/packages/rsx/src/hot_reloading_context.rs b/packages/rsx/src/hot_reloading_context.rs new file mode 100644 index 000000000..c38101093 --- /dev/null +++ b/packages/rsx/src/hot_reloading_context.rs @@ -0,0 +1,19 @@ +pub trait HotReloadingContext { + fn map_attribute( + element_name_rust: &str, + attribute_name_rust: &str, + ) -> Option<(&'static str, Option<&'static str>)>; + fn map_element(element_name_rust: &str) -> Option<(&'static str, Option<&'static str>)>; +} + +pub struct Empty; + +impl HotReloadingContext for Empty { + fn map_attribute(_: &str, _: &str) -> Option<(&'static str, Option<&'static str>)> { + None + } + + fn map_element(_: &str) -> Option<(&'static str, Option<&'static str>)> { + None + } +} diff --git a/packages/rsx/src/lib.rs b/packages/rsx/src/lib.rs index 65d48f18a..81babed6c 100644 --- a/packages/rsx/src/lib.rs +++ b/packages/rsx/src/lib.rs @@ -15,6 +15,7 @@ mod errors; mod component; mod element; +mod hot_reloading_context; mod ifmt; mod node; @@ -22,6 +23,7 @@ mod node; pub use component::*; use dioxus_core::{Template, TemplateAttribute, TemplateNode}; pub use element::*; +use hot_reloading_context::{Empty, HotReloadingContext}; pub use ifmt::*; pub use node::*; @@ -35,24 +37,29 @@ use syn::{ /// Fundametnally, every CallBody is a template #[derive(Default)] -pub struct CallBody { +pub struct CallBody { pub roots: Vec, // set this after pub inline_cx: bool, + + phantom: std::marker::PhantomData, } -impl CallBody { +impl CallBody { /// This function intentionally leaks memory to create a static template. /// Keeping the template static allows us to simplify the core of dioxus and leaking memory in dev mode is less of an issue. /// the previous_location is the location of the previous template at the time the template was originally compiled. pub fn leak_template(&self, previous_location: &'static str) -> Template { - let mut renderer = TemplateRenderer { roots: &self.roots }; + let mut renderer: TemplateRenderer = TemplateRenderer { + roots: &self.roots, + phantom: std::marker::PhantomData, + }; renderer.leak_template(previous_location) } } -impl Parse for CallBody { +impl Parse for CallBody { fn parse(input: ParseStream) -> Result { let mut roots = Vec::new(); @@ -69,14 +76,18 @@ impl Parse for CallBody { Ok(Self { roots, inline_cx: false, + phantom: std::marker::PhantomData, }) } } /// Serialize the same way, regardless of flavor -impl ToTokens for CallBody { +impl ToTokens for CallBody { fn to_tokens(&self, out_tokens: &mut TokenStream2) { - let body = TemplateRenderer { roots: &self.roots }; + let body: TemplateRenderer = TemplateRenderer { + roots: &self.roots, + phantom: std::marker::PhantomData, + }; if self.inline_cx { out_tokens.append_all(quote! { @@ -95,13 +106,14 @@ impl ToTokens for CallBody { } } -pub struct TemplateRenderer<'a> { +pub struct TemplateRenderer<'a, Ctx: HotReloadingContext = Empty> { pub roots: &'a [BodyNode], + phantom: std::marker::PhantomData, } -impl<'a> TemplateRenderer<'a> { +impl<'a, Ctx: HotReloadingContext> TemplateRenderer<'a, Ctx> { fn leak_template(&mut self, previous_location: &'static str) -> Template<'static> { - let mut context = DynamicContext::default(); + let mut context: DynamicContext = DynamicContext::default(); let roots: Vec<_> = self .roots @@ -138,9 +150,9 @@ impl<'a> TemplateRenderer<'a> { } } -impl<'a> ToTokens for TemplateRenderer<'a> { +impl<'a, Ctx: HotReloadingContext> ToTokens for TemplateRenderer<'a, Ctx> { fn to_tokens(&self, out_tokens: &mut TokenStream2) { - let mut context = DynamicContext::default(); + let mut context: DynamicContext = DynamicContext::default(); let key = match self.roots.get(0) { Some(BodyNode::Element(el)) if self.roots.len() == 1 => el.key.clone(), @@ -199,17 +211,31 @@ impl<'a> ToTokens for TemplateRenderer<'a> { } // As we print out the dynamic nodes, we want to keep track of them in a linear fashion // We'll use the size of the vecs to determine the index of the dynamic node in the final -#[derive(Default)] -pub struct DynamicContext<'a> { +pub struct DynamicContext<'a, Ctx: HotReloadingContext> { dynamic_nodes: Vec<&'a BodyNode>, dynamic_attributes: Vec<&'a ElementAttrNamed>, current_path: Vec, node_paths: Vec>, attr_paths: Vec>, + + phantom: std::marker::PhantomData, } -impl<'a> DynamicContext<'a> { +impl<'a, Ctx: HotReloadingContext> Default for DynamicContext<'a, Ctx> { + fn default() -> Self { + Self { + dynamic_nodes: Vec::new(), + dynamic_attributes: Vec::new(), + current_path: Vec::new(), + node_paths: Vec::new(), + attr_paths: Vec::new(), + phantom: std::marker::PhantomData, + } + } +} + +impl<'a, Ctx: HotReloadingContext> DynamicContext<'a, Ctx> { fn leak_node(&mut self, root: &'a BodyNode) -> TemplateNode<'static> { match root { BodyNode::Element(el) => { @@ -222,15 +248,24 @@ impl<'a> DynamicContext<'a> { // [0, 2] // [0, 2, 1] + let element_name_rust = el.name.to_string(); + let static_attrs: Vec<_> = el .attributes .iter() .map(|attr| match &attr.attr { - ElementAttr::AttrText { name: _, value } if value.is_static() => { + ElementAttr::AttrText { name, value } if value.is_static() => { let value = value.source.as_ref().unwrap(); + let attribute_name_rust = name.to_string(); + let (name, namespace) = + Ctx::map_attribute(&element_name_rust, &attribute_name_rust) + .unwrap_or(( + Box::leak(attribute_name_rust.into_boxed_str()), + None, + )); TemplateAttribute::Static { - name: "todo", - namespace: None, + name, + namespace, value: Box::leak(value.value().into_boxed_str()), // name: dioxus_elements::#el_name::#name.0, // namespace: dioxus_elements::#el_name::#name.1, @@ -277,15 +312,11 @@ impl<'a> DynamicContext<'a> { }) .collect(); - // TemplateNode::Element { - // tag: dioxus_elements::#el_name::TAG_NAME, - // namespace: dioxus_elements::#el_name::NAME_SPACE, - // attrs: &[ #attrs ], - // children: &[ #children ], - // } + let (tag, namespace) = Ctx::map_element(&element_name_rust) + .unwrap_or((Box::leak(element_name_rust.into_boxed_str()), None)); TemplateNode::Element { - tag: "todo", - namespace: None, + tag, + namespace, attrs: Box::leak(static_attrs.into_boxed_slice()), children: Box::leak(children.into_boxed_slice()), } @@ -420,7 +451,7 @@ impl<'a> DynamicContext<'a> { #[test] fn template() { let input = quote! { - div { + svg { width: 100, height: "100px", "width2": 100, @@ -431,7 +462,33 @@ fn template() { (0..10).map(|i| rsx!{"{i}"}) } }; - let call_body: CallBody = syn::parse2(input).unwrap(); + + struct Mock; + + impl HotReloadingContext for Mock { + fn map_attribute( + element_name_rust: &str, + attribute_name_rust: &str, + ) -> Option<(&'static str, Option<&'static str>)> { + match element_name_rust { + "svg" => match attribute_name_rust { + "width" => Some(("width", Some("style"))), + "height" => Some(("height", Some("style"))), + _ => None, + }, + _ => None, + } + } + + fn map_element(element_name_rust: &str) -> Option<(&'static str, Option<&'static str>)> { + match element_name_rust { + "svg" => Some(("svg", Some("svg"))), + _ => None, + } + } + } + + let call_body: CallBody = syn::parse2(input).unwrap(); let template = call_body.leak_template("testing"); @@ -442,13 +499,13 @@ fn template() { Template { name: "testing", roots: &[TemplateNode::Element { - tag: "div", - namespace: None, + tag: "svg", + namespace: Some("svg"), attrs: &[ TemplateAttribute::Dynamic { id: 0 }, TemplateAttribute::Static { name: "height", - namespace: None, + namespace: Some("style"), value: "100px", }, TemplateAttribute::Dynamic { id: 1 }, diff --git a/packages/rsx/src/node.rs b/packages/rsx/src/node.rs index cb604654e..4950c857c 100644 --- a/packages/rsx/src/node.rs +++ b/packages/rsx/src/node.rs @@ -1,3 +1,5 @@ +use std::marker::PhantomData; + use super::*; use proc_macro2::{Span, TokenStream as TokenStream2}; @@ -141,7 +143,10 @@ impl ToTokens for BodyNode { pat, expr, body, .. } = exp; - let renderer = TemplateRenderer { roots: body }; + let renderer: TemplateRenderer = TemplateRenderer { + roots: body, + phantom: PhantomData, + }; tokens.append_all(quote! { __cx.make_node( From 6b19229b533bb069498684baaec37cd63417ceff Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Sat, 10 Dec 2022 16:21:31 -0600 Subject: [PATCH 008/432] use interning to leak less memory --- packages/rsx/Cargo.toml | 1 + packages/rsx/src/lib.rs | 79 +++++++++++++++++++++++++---------------- 2 files changed, 49 insertions(+), 31 deletions(-) diff --git a/packages/rsx/Cargo.toml b/packages/rsx/Cargo.toml index 40b785b6e..40655ed77 100644 --- a/packages/rsx/Cargo.toml +++ b/packages/rsx/Cargo.toml @@ -12,3 +12,4 @@ syn = { version = "1.0", features = ["full", "extra-traits"] } quote = { version = "1.0" } dioxus-core = { path = "../core", features = ["serialize"] } serde = { version = "1.0", features = ["derive"] } +internment = "0.7.0" \ No newline at end of file diff --git a/packages/rsx/src/lib.rs b/packages/rsx/src/lib.rs index 81babed6c..c6dbd5862 100644 --- a/packages/rsx/src/lib.rs +++ b/packages/rsx/src/lib.rs @@ -19,12 +19,15 @@ mod hot_reloading_context; mod ifmt; mod node; +use std::{borrow::Borrow, hash::Hash}; + // Re-export the namespaces into each other pub use component::*; use dioxus_core::{Template, TemplateAttribute, TemplateNode}; pub use element::*; use hot_reloading_context::{Empty, HotReloadingContext}; pub use ifmt::*; +use internment::Intern; pub use node::*; // imports @@ -35,6 +38,13 @@ use syn::{ Result, Token, }; +// interns a object into a static object, resusing the value if it already exists +fn intern<'a, T: Eq + Hash + Send + Sync + ?Sized + 'static>( + s: impl Into>, +) -> &'static T { + s.into().as_ref() +} + /// Fundametnally, every CallBody is a template #[derive(Default)] pub struct CallBody { @@ -47,15 +57,20 @@ pub struct CallBody { } impl CallBody { + /// This will try to create a new template from the current body and the previous body. This will return None if the rsx has some dynamic part that has changed. /// This function intentionally leaks memory to create a static template. /// Keeping the template static allows us to simplify the core of dioxus and leaking memory in dev mode is less of an issue. /// the previous_location is the location of the previous template at the time the template was originally compiled. - pub fn leak_template(&self, previous_location: &'static str) -> Template { + pub fn update_template( + &self, + template: Option<&CallBody>, + location: &'static str, + ) -> Option