Merge branch 'master' into jk/autofmt-fixues

This commit is contained in:
Jonathan Kelley committed 2022-12-19 12:19:51 -08:00
commit 09b38574db
336 files changed
+19973 -16881

No files matched your search

+892
View File
@@ -0,0 +1,892 @@
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
/// <p class="note editorial">Above point sounds a bit obvious. Remove/rewrite?</p>
/// ```
///
/// ### 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",
}
}
+34 -26
View File
@@ -23,7 +23,7 @@ use syn::{
Token,
};
#[derive(PartialEq, Eq, Debug)]
#[derive(PartialEq, Eq, Clone, Debug, Hash)]
pub struct Component {
pub name: syn::Path,
pub prop_gen_args: Option<AngleBracketedGenericArguments>,
@@ -55,6 +55,18 @@ impl Component {
Ok(())
}
pub fn key(&self) -> Option<&IfmtInput> {
match self
.fields
.iter()
.find(|f| f.name == "key")
.map(|f| &f.content)
{
Some(ContentField::Formatted(fmt)) => Some(fmt),
_ => None,
}
}
}
impl Parse for Component {
@@ -117,8 +129,6 @@ impl ToTokens for Component {
let name = &self.name;
let prop_gen_args = &self.prop_gen_args;
let mut has_key = None;
let builder = match &self.manual_props {
Some(manual_props) => {
let mut toks = quote! {
@@ -126,7 +136,7 @@ impl ToTokens for Component {
};
for field in &self.fields {
if field.name == "key" {
has_key = Some(field);
// skip keys
} else {
let name = &field.name;
let val = &field.content;
@@ -144,23 +154,25 @@ impl ToTokens for Component {
}
None => {
let mut toks = match prop_gen_args {
Some(gen_args) => quote! { fc_to_builder #gen_args(#name) },
Some(gen_args) => quote! { fc_to_builder(#name #gen_args) },
None => quote! { fc_to_builder(#name) },
};
for field in &self.fields {
match field.name.to_string().as_str() {
"key" => {
//
has_key = Some(field);
}
"key" => {}
_ => toks.append_all(quote! {#field}),
}
}
if !self.children.is_empty() {
let childs = &self.children;
let renderer = TemplateRenderer {
roots: &self.children,
};
toks.append_all(quote! {
.children(__cx.create_children([ #( #childs ),* ]))
.children(
Ok({ #renderer })
)
});
}
@@ -171,21 +183,17 @@ impl ToTokens for Component {
}
};
let key_token = match has_key {
Some(field) => {
let inners = &field.content;
quote! { Some(format_args_f!(#inners)) }
}
None => quote! { None },
};
let fn_name = self.name.segments.last().unwrap().ident.to_string();
let gen_name = match &self.prop_gen_args {
Some(gen) => quote! { #name #gen },
None => quote! { #name },
};
tokens.append_all(quote! {
__cx.component(
#name,
#gen_name,
#builder,
#key_token,
#fn_name
)
})
@@ -193,16 +201,16 @@ impl ToTokens for Component {
}
// the struct's fields info
#[derive(PartialEq, Eq, Debug)]
#[derive(PartialEq, Eq, Clone, Debug, Hash)]
pub struct ComponentField {
pub name: Ident,
pub content: ContentField,
}
#[derive(PartialEq, Eq, Debug)]
#[derive(PartialEq, Eq, Clone, Debug, Hash)]
pub enum ContentField {
ManExpr(Expr),
Formatted(LitStr),
Formatted(IfmtInput),
OnHandlerRaw(Expr),
}
@@ -211,7 +219,7 @@ impl ToTokens for ContentField {
match self {
ContentField::ManExpr(e) => e.to_tokens(tokens),
ContentField::Formatted(s) => tokens.append_all(quote! {
__cx.raw_text(format_args_f!(#s)).0
__cx.raw_text(#s)
}),
ContentField::OnHandlerRaw(e) => tokens.append_all(quote! {
__cx.event_handler(#e)
@@ -231,7 +239,7 @@ impl Parse for ComponentField {
}
if name == "key" {
let content = ContentField::ManExpr(input.parse()?);
let content = ContentField::Formatted(input.parse()?);
return Ok(Self { name, content });
}
if input.peek(LitStr) {
+46 -24
View File
@@ -10,10 +10,10 @@ use syn::{
// =======================================
// Parse the VNode::Element type
// =======================================
#[derive(PartialEq, Eq, Debug)]
#[derive(PartialEq, Eq, Clone, Debug, Hash)]
pub struct Element {
pub name: Ident,
pub key: Option<LitStr>,
pub key: Option<IfmtInput>,
pub attributes: Vec<ElementAttrNamed>,
pub children: Vec<BodyNode>,
pub _is_static: bool,
@@ -45,15 +45,14 @@ impl Parse for Element {
content.parse::<Token![:]>()?;
if content.peek(LitStr) && content.peek2(Token![,]) {
let value = content.parse::<LitStr>()?;
if content.peek(LitStr) {
let value = content.parse()?;
attributes.push(ElementAttrNamed {
el_name: el_name.clone(),
attr: ElementAttr::CustomAttrText { name, value },
});
} else {
let value = content.parse::<Expr>()?;
attributes.push(ElementAttrNamed {
el_name: el_name.clone(),
attr: ElementAttr::CustomAttrExpression { name, value },
@@ -164,7 +163,7 @@ impl ToTokens for Element {
let children = &self.children;
let key = match &self.key {
Some(ty) => quote! { Some(format_args_f!(#ty)) },
Some(ty) => quote! { Some(#ty) },
None => quote! { None },
};
@@ -190,18 +189,18 @@ impl ToTokens for Element {
}
}
#[derive(PartialEq, Eq, Debug)]
#[derive(PartialEq, Eq, Clone, Debug, Hash)]
pub enum ElementAttr {
/// attribute: "valuee {}"
AttrText { name: Ident, value: LitStr },
/// `attribute: "value"`
AttrText { name: Ident, value: IfmtInput },
/// attribute: true,
/// `attribute: true`
AttrExpression { name: Ident, value: Expr },
/// "attribute": "value {}"
CustomAttrText { name: LitStr, value: LitStr },
/// `"attribute": "value"`
CustomAttrText { name: LitStr, value: IfmtInput },
/// "attribute": true,
/// `"attribute": true`
CustomAttrExpression { name: LitStr, value: Expr },
// /// onclick: move |_| {}
@@ -231,7 +230,7 @@ impl ElementAttr {
}
}
#[derive(PartialEq, Eq, Debug)]
#[derive(PartialEq, Eq, Clone, Debug, Hash)]
pub struct ElementAttrNamed {
pub el_name: Ident,
pub attr: ElementAttr,
@@ -244,34 +243,57 @@ impl ToTokens for ElementAttrNamed {
tokens.append_all(match attr {
ElementAttr::AttrText { name, value } => {
quote! {
dioxus_elements::#el_name.#name(__cx, format_args_f!(#value))
__cx.attr(
dioxus_elements::#el_name::#name.0,
#value,
None,
false
)
}
}
ElementAttr::AttrExpression { name, value } => {
quote! {
dioxus_elements::#el_name.#name(__cx, #value)
__cx.attr(
dioxus_elements::#el_name::#name.0,
#value,
None,
false
)
}
}
ElementAttr::CustomAttrText { name, value } => {
quote! {
__cx.attr( #name, format_args_f!(#value), None, false )
__cx.attr(
dioxus_elements::#el_name::#name.0,
#value,
None,
false
)
}
}
ElementAttr::CustomAttrExpression { name, value } => {
quote! {
__cx.attr( #name, format_args_f!(#value), None, false )
__cx.attr(
dioxus_elements::#el_name::#name.0,
#value,
None,
false
)
}
}
// ElementAttr::EventClosure { name, closure } => {
// quote! {
// dioxus_elements::on::#name(__cx, #closure)
// }
// }
ElementAttr::EventTokens { name, tokens } => {
quote! {
dioxus_elements::on::#name(__cx, #tokens)
dioxus_elements::events::#name(__cx, #tokens)
}
}
});
}
}
// ::dioxus::core::Attribute {
// name: stringify!(#name),
// namespace: None,
// volatile: false,
// mounted_node: Default::default(),
// value: ::dioxus::core::AttributeValue::Text(#value),
// }
+62
View File
@@ -0,0 +1,62 @@
use std::fmt::Display;
use serde::{Deserialize, Serialize};
/// An error produced when interperting the rsx
#[derive(Debug, Serialize, Deserialize)]
pub enum Error {
ParseError(ParseError),
RecompileRequiredError(RecompileReason),
}
#[derive(Debug, Serialize, Deserialize)]
pub enum RecompileReason {
Variable(String),
Expression(String),
Component(String),
Listener(String),
Attribute(String),
}
#[derive(Debug, Serialize, Deserialize)]
pub struct CodeLocation {
pub line: u32,
pub column: u32,
pub file_path: &'static str,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct ParseError {
pub message: String,
pub location: CodeLocation,
}
impl ParseError {
pub fn new(error: syn::Error, mut location: CodeLocation) -> Self {
let message = error.to_string();
let syn_call_site = error.span().start();
location.line += syn_call_site.line as u32;
if syn_call_site.line == 0 {
location.column += syn_call_site.column as u32;
} else {
location.column = syn_call_site.column as u32;
}
location.column += 1;
ParseError { message, location }
}
}
impl Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Error::ParseError(error) => writeln!(
f,
"parse error:\n--> at {}:{}:{}\n\t{:?}",
error.location.file_path, error.location.line, error.location.column, error.message
),
Error::RecompileRequiredError(reason) => {
writeln!(f, "recompile required: {:?}", reason)
}
}
}
}
+139 -89
View File
@@ -2,87 +2,43 @@ use std::{collections::HashSet, str::FromStr};
use proc_macro2::{Span, TokenStream};
use quote::{quote, ToTokens};
use quote::{quote, ToTokens, TokenStreamExt};
use syn::{
parse::{Parse, ParseStream},
*,
};
pub fn format_args_f_impl(input: IfmtInput) -> Result<TokenStream> {
// build format_literal
let mut format_literal = String::new();
let mut expr_counter = 0;
for segment in input.segments.iter() {
match segment {
Segment::Literal(s) => format_literal += &s.replace('{', "{{").replace('}', "}}"),
Segment::Formatted {
format_args,
segment,
} => {
format_literal += "{";
match segment {
FormattedSegment::Expr(_) => {
format_literal += &expr_counter.to_string();
expr_counter += 1;
}
FormattedSegment::Ident(ident) => {
format_literal += &ident.to_string();
}
}
format_literal += ":";
format_literal += format_args;
format_literal += "}";
}
}
}
let positional_args = input.segments.iter().filter_map(|seg| {
if let Segment::Formatted {
segment: FormattedSegment::Expr(expr),
..
} = seg
{
Some(expr)
} else {
None
}
});
// remove duplicate idents
let named_args_idents: HashSet<_> = input
.segments
.iter()
.filter_map(|seg| {
if let Segment::Formatted {
segment: FormattedSegment::Ident(ident),
..
} = seg
{
Some(ident)
} else {
None
}
})
.collect();
let named_args = named_args_idents
.iter()
.map(|ident| quote!(#ident = #ident));
Ok(quote! {
format_args!(
#format_literal
#(, #positional_args)*
#(, #named_args)*
)
})
Ok(input.into_token_stream())
}
#[allow(dead_code)] // dumb compiler does not see the struct being used...
#[derive(Debug)]
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub struct IfmtInput {
pub source: Option<LitStr>,
pub segments: Vec<Segment>,
}
impl IfmtInput {
pub fn is_static(&self) -> bool {
matches!(self.segments.as_slice(), &[Segment::Literal(_)])
}
}
impl IfmtInput {
pub fn to_static(&self) -> Option<String> {
self.segments
.iter()
.try_fold(String::new(), |acc, segment| {
if let Segment::Literal(seg) = segment {
Some(acc + seg)
} else {
None
}
})
}
}
impl FromStr for IfmtInput {
type Err = syn::Error;
@@ -96,7 +52,9 @@ impl FromStr for IfmtInput {
current_literal.push(c);
continue;
}
segments.push(Segment::Literal(current_literal));
if !current_literal.is_empty() {
segments.push(Segment::Literal(current_literal));
}
current_literal = String::new();
let mut current_captured = String::new();
while let Some(c) = chars.next() {
@@ -104,10 +62,10 @@ impl FromStr for IfmtInput {
let mut current_format_args = String::new();
for c in chars.by_ref() {
if c == '}' {
segments.push(Segment::Formatted {
segments.push(Segment::Formatted(FormattedSegment {
format_args: current_format_args,
segment: FormattedSegment::parse(&current_captured)?,
});
segment: FormattedSegmentType::parse(&current_captured)?,
}));
break;
}
current_format_args.push(c);
@@ -115,10 +73,10 @@ impl FromStr for IfmtInput {
break;
}
if c == '}' {
segments.push(Segment::Formatted {
segments.push(Segment::Formatted(FormattedSegment {
format_args: String::new(),
segment: FormattedSegment::parse(&current_captured)?,
});
segment: FormattedSegmentType::parse(&current_captured)?,
}));
break;
}
current_captured.push(c);
@@ -138,27 +96,117 @@ impl FromStr for IfmtInput {
current_literal.push(c);
}
}
segments.push(Segment::Literal(current_literal));
Ok(Self { segments })
if !current_literal.is_empty() {
segments.push(Segment::Literal(current_literal));
}
Ok(Self {
segments,
source: None,
})
}
}
#[derive(Debug)]
pub enum Segment {
Literal(String),
Formatted {
format_args: String,
segment: FormattedSegment,
},
impl ToTokens for IfmtInput {
fn to_tokens(&self, tokens: &mut TokenStream) {
// build format_literal
let mut format_literal = String::new();
let mut expr_counter = 0;
for segment in self.segments.iter() {
match segment {
Segment::Literal(s) => format_literal += &s.replace('{', "{{").replace('}', "}}"),
Segment::Formatted(FormattedSegment {
format_args,
segment,
}) => {
format_literal += "{";
match segment {
FormattedSegmentType::Expr(_) => {
format_literal += &expr_counter.to_string();
expr_counter += 1;
}
FormattedSegmentType::Ident(ident) => {
format_literal += &ident.to_string();
}
}
format_literal += ":";
format_literal += format_args;
format_literal += "}";
}
}
}
let positional_args = self.segments.iter().filter_map(|seg| {
if let Segment::Formatted(FormattedSegment {
segment: FormattedSegmentType::Expr(expr),
..
}) = seg
{
Some(expr)
} else {
None
}
});
// remove duplicate idents
let named_args_idents: HashSet<_> = self
.segments
.iter()
.filter_map(|seg| {
if let Segment::Formatted(FormattedSegment {
segment: FormattedSegmentType::Ident(ident),
..
}) = seg
{
Some(ident)
} else {
None
}
})
.collect();
let named_args = named_args_idents
.iter()
.map(|ident| quote!(#ident = #ident));
quote! {
format_args!(
#format_literal
#(, #positional_args)*
#(, #named_args)*
)
}
.to_tokens(tokens)
}
}
#[derive(Debug)]
pub enum FormattedSegment {
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub enum Segment {
Literal(String),
Formatted(FormattedSegment),
}
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub struct FormattedSegment {
format_args: String,
segment: FormattedSegmentType,
}
impl ToTokens for FormattedSegment {
fn to_tokens(&self, tokens: &mut TokenStream) {
let (fmt, seg) = (&self.format_args, &self.segment);
let fmt = format!("{{0:{}}}", fmt);
tokens.append_all(quote! {
format_args!(#fmt, #seg)
});
}
}
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub enum FormattedSegmentType {
Expr(Box<Expr>),
Ident(Ident),
}
impl FormattedSegment {
impl FormattedSegmentType {
fn parse(input: &str) -> Result<Self> {
if let Ok(ident) = parse_str::<Ident>(input) {
if ident == input {
@@ -176,7 +224,7 @@ impl FormattedSegment {
}
}
impl ToTokens for FormattedSegment {
impl ToTokens for FormattedSegmentType {
fn to_tokens(&self, tokens: &mut TokenStream) {
match self {
Self::Expr(expr) => expr.to_tokens(tokens),
@@ -189,6 +237,8 @@ impl Parse for IfmtInput {
fn parse(input: ParseStream) -> Result<Self> {
let input: LitStr = input.parse()?;
let input_str = input.value();
IfmtInput::from_str(&input_str)
let mut ifmt = IfmtInput::from_str(&input_str)?;
ifmt.source = Some(input);
Ok(ifmt)
}
}
+209 -17
View File
@@ -13,14 +13,14 @@
#[macro_use]
mod errors;
mod component;
mod element;
mod ifmt;
mod node;
mod whitespace;
mod raw_expr;
mod template;
mod text;
mod whitespace;
// Re-export the namespaces into each other
pub use component::*;
@@ -36,9 +36,13 @@ use syn::{
Result, Token,
};
#[derive(Debug)]
/// Fundametnally, every CallBody is a template
#[derive(Default, Debug)]
pub struct CallBody {
pub roots: Vec<BodyNode>,
// set this after
pub inline_cx: bool,
}
impl Parse for CallBody {
@@ -55,27 +59,215 @@ impl Parse for CallBody {
roots.push(node);
}
Ok(Self { roots })
Ok(Self {
roots,
inline_cx: false,
})
}
}
/// Serialize the same way, regardless of flavor
impl ToTokens for CallBody {
fn to_tokens(&self, out_tokens: &mut TokenStream2) {
let inner = if self.roots.len() == 1 {
let inner = &self.roots[0];
quote! { #inner }
} else {
let childs = &self.roots;
quote! { __cx.fragment_root([ #(#childs),* ]) }
};
let body = TemplateRenderer { roots: &self.roots };
// Otherwise we just build the LazyNode wrapper
out_tokens.append_all(quote! {
LazyNodes::new(move |__cx: NodeFactory| -> VNode {
use dioxus_elements::{GlobalAttributes, SvgAttributes};
#inner
if self.inline_cx {
out_tokens.append_all(quote! {
Ok({
let __cx = cx;
#body
})
})
})
} else {
out_tokens.append_all(quote! {
::dioxus::core::LazyNodes::new( move | __cx: &::dioxus::core::ScopeState| -> ::dioxus::core::VNode {
#body
})
})
}
}
}
pub struct TemplateRenderer<'a> {
pub roots: &'a [BodyNode],
}
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 key = match self.roots.get(0) {
Some(BodyNode::Element(el)) if self.roots.len() == 1 => el.key.clone(),
Some(BodyNode::Component(comp)) if self.roots.len() == 1 => comp.key().cloned(),
_ => None,
};
let key_tokens = match key {
Some(tok) => quote! { Some( __cx.raw_text(#tok) ) },
None => quote! { None },
};
let spndbg = format!("{:?}", self.roots[0].span());
let root_col = spndbg[9..].split("..").next().unwrap();
let root_printer = self.roots.iter().enumerate().map(|(idx, root)| {
context.current_path.push(idx as u8);
let out = context.render_static_node(root);
context.current_path.pop();
out
});
// Render and release the mutable borrow on context
let num_roots = self.roots.len();
let roots = quote! { #( #root_printer ),* };
let node_printer = &context.dynamic_nodes;
let dyn_attr_printer = &context.dynamic_attributes;
let node_paths = context.node_paths.iter().map(|it| quote!(&[#(#it),*]));
let attr_paths = context.attr_paths.iter().map(|it| quote!(&[#(#it),*]));
out_tokens.append_all(quote! {
static TEMPLATE: ::dioxus::core::Template = ::dioxus::core::Template {
name: concat!(
file!(),
":",
line!(),
":",
column!(),
":",
#root_col
),
roots: &[ #roots ],
node_paths: &[ #(#node_paths),* ],
attr_paths: &[ #(#attr_paths),* ],
};
::dioxus::core::VNode {
parent: None,
key: #key_tokens,
template: TEMPLATE,
root_ids: std::cell::Cell::from_mut( __cx.bump().alloc([None; #num_roots]) as &mut _).as_slice_of_cells(),
// root_ids: std::cell::Cell::from_mut( __cx.bump().alloc([None; #num_roots]) as &mut [::dioxus::core::ElementId]).as_slice_of_cells(),
dynamic_nodes: __cx.bump().alloc([ #( #node_printer ),* ]),
dynamic_attrs: __cx.bump().alloc([ #( #dyn_attr_printer ),* ]),
}
});
}
}
// 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
pub struct DynamicContext<'a> {
dynamic_nodes: Vec<&'a BodyNode>,
dynamic_attributes: Vec<&'a ElementAttrNamed>,
current_path: Vec<u8>,
node_paths: Vec<Vec<u8>>,
attr_paths: Vec<Vec<u8>>,
}
impl<'a> DynamicContext<'a> {
fn render_static_node(&mut self, root: &'a BodyNode) -> TokenStream2 {
match root {
BodyNode::Element(el) => {
let el_name = &el.name;
// dynamic attributes
// [0]
// [0, 1]
// [0, 1]
// [0, 1]
// [0, 1, 2]
// [0, 2]
// [0, 2, 1]
let static_attrs = el.attributes.iter().map(|attr| match &attr.attr {
ElementAttr::AttrText { name, value } if value.is_static() => {
let value = value.to_static().unwrap();
quote! {
::dioxus::core::TemplateAttribute::Static {
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.to_static().unwrap();
quote! {
::dioxus::core::TemplateAttribute::Static {
name: #name,
namespace: None,
value: #value,
// 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());
quote! { ::dioxus::core::TemplateAttribute::Dynamic { id: #ct } }
}
});
let attrs = quote! { #(#static_attrs),*};
let children = el.children.iter().enumerate().map(|(idx, root)| {
self.current_path.push(idx as u8);
let out = self.render_static_node(root);
self.current_path.pop();
out
});
let _opt = el.children.len() == 1;
let children = quote! { #(#children),* };
quote! {
::dioxus::core::TemplateNode::Element {
tag: dioxus_elements::#el_name::TAG_NAME,
namespace: dioxus_elements::#el_name::NAME_SPACE,
attrs: &[ #attrs ],
children: &[ #children ],
}
}
}
BodyNode::Text(text) if text.is_static() => {
let text = text.to_static().unwrap();
quote! { ::dioxus::core::TemplateNode::Text{ text: #text } }
}
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(_) => {
quote! { ::dioxus::core::TemplateNode::DynamicText { id: #ct } }
}
_ => quote! { ::dioxus::core::TemplateNode::Dynamic { id: #ct } },
}
}
}
}
}
+147 -15
View File
@@ -5,9 +5,10 @@ use super::*;
use proc_macro2::{Span, TokenStream as TokenStream2};
use quote::{quote, ToTokens, TokenStreamExt};
use syn::{
braced,
parse::{Parse, ParseStream},
spanned::Spanned,
token, LitStr, Result,
token, Expr, ExprIf, LitStr, Pat, Result,
};
/*
@@ -18,12 +19,14 @@ Parse
-> "text {with_args}"
-> (0..10).map(|f| rsx!("asd")), // <--- notice the comma - must be a complete expr
*/
#[derive(Debug, PartialEq, Eq)]
#[derive(PartialEq, Eq, Clone, Debug, Hash)]
pub enum BodyNode {
Element(Element),
Component(Component),
Text(TextNode),
RawExpr(RawExprNode),
ForLoop(ForLoop),
IfChain(ExprIf),
Text(IfmtInput),
RawExpr(Expr),
}
impl BodyNode {
@@ -35,8 +38,10 @@ impl BodyNode {
match self {
BodyNode::Element(el) => el.name.span(),
BodyNode::Component(component) => component.name.span(),
BodyNode::Text(text) => text.span(),
BodyNode::Text(text) => text.source.span(),
BodyNode::RawExpr(exp) => exp.span(),
BodyNode::ForLoop(fl) => fl.for_token.span(),
BodyNode::IfChain(f) => f.if_token.span(),
}
}
}
@@ -53,17 +58,18 @@ impl Parse for BodyNode {
// - one ident
// - followed by `{`
// - 1st char is lowercase
// - no underscores (reserved for components)
//
// example:
// div {}
if let Some(ident) = path.get_ident() {
let el_name = ident.to_string();
let first_char = el_name.chars().next().unwrap();
if body_stream.peek(token::Brace)
&& ident
.to_string()
.chars()
.next()
.unwrap()
.is_ascii_lowercase()
&& first_char.is_ascii_lowercase()
&& !el_name.contains('_')
{
return Ok(BodyNode::Element(stream.parse::<Element>()?));
}
@@ -89,7 +95,35 @@ impl Parse for BodyNode {
}
}
Ok(BodyNode::RawExpr(stream.parse()?))
// Transform for loops into into_iter calls
if stream.peek(Token![for]) {
let _f = stream.parse::<Token![for]>()?;
let pat = stream.parse::<Pat>()?;
let _i = stream.parse::<Token![in]>()?;
let expr = stream.parse::<Box<Expr>>()?;
let body;
braced!(body in stream);
let mut children = vec![];
while !body.is_empty() {
children.push(body.parse()?);
}
return Ok(BodyNode::ForLoop(ForLoop {
for_token: _f,
pat,
in_token: _i,
expr,
body: children,
}));
}
// Transform unterminated if statements into terminated optional if statements
if stream.peek(Token![if]) {
return Ok(BodyNode::IfChain(stream.parse()?));
}
Ok(BodyNode::RawExpr(stream.parse::<Expr>()?))
}
}
@@ -98,12 +132,110 @@ impl ToTokens for BodyNode {
match &self {
BodyNode::Element(el) => el.to_tokens(tokens),
BodyNode::Component(comp) => comp.to_tokens(tokens),
BodyNode::Text(text) => tokens.append_all(quote! {
__cx.text(format_args_f!(#text))
BodyNode::Text(txt) => tokens.append_all(quote! {
__cx.text_node(#txt)
}),
BodyNode::RawExpr(exp) => tokens.append_all(quote! {
__cx.fragment_from_iter(#exp)
__cx.make_node(#exp)
}),
BodyNode::ForLoop(exp) => {
let ForLoop {
pat, expr, body, ..
} = exp;
let renderer = TemplateRenderer { roots: body };
tokens.append_all(quote! {
__cx.make_node(
(#expr).into_iter().map(|#pat| { #renderer })
)
})
}
BodyNode::IfChain(chain) => {
if is_if_chain_terminated(chain) {
tokens.append_all(quote! {
__cx.make_node(#chain)
});
} else {
let ExprIf {
cond,
then_branch,
else_branch,
..
} = chain;
let mut body = TokenStream2::new();
body.append_all(quote! {
if #cond {
Some(#then_branch)
}
});
let mut elif = else_branch;
while let Some((_, ref branch)) = elif {
match branch.as_ref() {
Expr::If(ref eelif) => {
let ExprIf {
cond,
then_branch,
else_branch,
..
} = eelif;
body.append_all(quote! {
else if #cond {
Some(#then_branch)
}
});
elif = else_branch;
}
_ => {
body.append_all(quote! {
else {
#branch
}
});
break;
}
}
}
body.append_all(quote! {
else { None }
});
tokens.append_all(quote! {
__cx.make_node(#body)
});
}
}
}
}
}
#[derive(PartialEq, Eq, Clone, Debug, Hash)]
pub struct ForLoop {
pub for_token: Token![for],
pub pat: Pat,
pub in_token: Token![in],
pub expr: Box<Expr>,
pub body: Vec<BodyNode>,
}
fn is_if_chain_terminated(chain: &ExprIf) -> bool {
let mut current = chain;
loop {
if let Some((_, else_block)) = &current.else_branch {
if let Expr::If(else_if) = else_block.as_ref() {
current = else_if;
} else {
return true;
}
} else {
return false;
}
}
}
File diff suppressed because it is too large. Load diff