Skip to content

Commit

Permalink
Merge pull request #1463 from ealmloff/many_optional_attributes
Browse files Browse the repository at this point in the history
Implement multiple optional attributes
  • Loading branch information
jkelleyrtp authored Jan 4, 2024
2 parents 70ff508 + 8df7a76 commit d933291
Show file tree
Hide file tree
Showing 15 changed files with 549 additions and 324 deletions.
5 changes: 5 additions & 0 deletions examples/rsx_usage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ fn App(cx: Scope) -> Element {
let formatting = "formatting!";
let formatting_tuple = ("a", "b");
let lazy_fmt = format_args!("lazily formatted text");
let asd = 123;
cx.render(rsx! {
div {
// Elements
Expand Down Expand Up @@ -80,6 +81,10 @@ fn App(cx: Scope) -> Element {
// pass simple rust expressions in
class: lazy_fmt,
id: format_args!("attributes can be passed lazily with std::fmt::Arguments"),
class: "asd",
class: "{asd}",
// if statements can be used to conditionally render attributes
class: if formatting.contains("form") { "{asd}" },
div {
class: {
const WORD: &str = "expressions";
Expand Down
6 changes: 5 additions & 1 deletion examples/tailwind/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,13 @@ fn main() {
}

pub fn app(cx: Scope) -> Element {
let grey_background = true;
cx.render(rsx!(
div {
header { class: "text-gray-400 bg-gray-900 body-font",
header {
class: "text-gray-400 body-font",
// you can use optional attributes to optionally apply a tailwind class
class: if grey_background { "bg-gray-900" },
div { class: "container mx-auto flex flex-wrap p-5 flex-col md:flex-row items-center",
a { class: "flex title-font font-medium items-center text-white mb-4 md:mb-0",
StacksIcon {}
Expand Down
66 changes: 41 additions & 25 deletions packages/autofmt/src/element.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ impl Writer<'_> {
attributes,
children,
brace,
..
} = el;

/*
Expand Down Expand Up @@ -209,22 +210,44 @@ impl Writer<'_> {
Ok(())
}

fn write_attribute(&mut self, attr: &ElementAttrNamed) -> Result {
match &attr.attr {
ElementAttr::AttrText { name, value } => {
write!(self.out, "{name}: {value}", value = ifmt_to_string(value))?;
fn write_attribute_name(&mut self, attr: &ElementAttrName) -> Result {
match attr {
ElementAttrName::BuiltIn(name) => {
write!(self.out, "{}", name)?;
}
ElementAttrName::Custom(name) => {
write!(self.out, "{}", name.to_token_stream())?;
}
ElementAttr::AttrExpression { name, value } => {
}

Ok(())
}

fn write_attribute_value(&mut self, value: &ElementAttrValue) -> Result {
match value {
ElementAttrValue::AttrOptionalExpr { condition, value } => {
write!(
self.out,
"if {condition} {{ ",
condition = prettyplease::unparse_expr(condition),
)?;
self.write_attribute_value(value)?;
write!(self.out, " }}")?;
}
ElementAttrValue::AttrLiteral(value) => {
write!(self.out, "{value}", value = ifmt_to_string(value))?;
}
ElementAttrValue::AttrExpr(value) => {
let out = prettyplease::unparse_expr(value);
let mut lines = out.split('\n').peekable();
let first = lines.next().unwrap();

// a one-liner for whatever reason
// Does not need a new line
if lines.peek().is_none() {
write!(self.out, "{name}: {first}")?;
write!(self.out, "{first}")?;
} else {
writeln!(self.out, "{name}: {first}")?;
writeln!(self.out, "{first}")?;

while let Some(line) = lines.next() {
self.out.indented_tab()?;
Expand All @@ -237,22 +260,7 @@ impl Writer<'_> {
}
}
}

ElementAttr::CustomAttrText { name, value } => {
write!(
self.out,
"{name}: {value}",
name = name.to_token_stream(),
value = ifmt_to_string(value)
)?;
}

ElementAttr::CustomAttrExpression { name, value } => {
let out = prettyplease::unparse_expr(value);
write!(self.out, "{}: {}", name.to_token_stream(), out)?;
}

ElementAttr::EventTokens { name, tokens } => {
ElementAttrValue::EventTokens(tokens) => {
let out = self.retrieve_formatted_expr(tokens).to_string();

let mut lines = out.split('\n').peekable();
Expand All @@ -261,9 +269,9 @@ impl Writer<'_> {
// a one-liner for whatever reason
// Does not need a new line
if lines.peek().is_none() {
write!(self.out, "{name}: {first}")?;
write!(self.out, "{first}")?;
} else {
writeln!(self.out, "{name}: {first}")?;
writeln!(self.out, "{first}")?;

while let Some(line) = lines.next() {
self.out.indented_tab()?;
Expand All @@ -281,6 +289,14 @@ impl Writer<'_> {
Ok(())
}

fn write_attribute(&mut self, attr: &ElementAttrNamed) -> Result {
self.write_attribute_name(&attr.attr.name)?;
write!(self.out, ": ")?;
self.write_attribute_value(&attr.attr.value)?;

Ok(())
}

// make sure the comments are actually relevant to this element.
// test by making sure this element is the primary element on this line
pub fn current_span_is_primary(&self, location: Span) -> bool {
Expand Down
78 changes: 44 additions & 34 deletions packages/autofmt/src/writer.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use dioxus_rsx::{BodyNode, ElementAttr, ElementAttrNamed, ForLoop};
use dioxus_rsx::{BodyNode, ElementAttrNamed, ElementAttrValue, ForLoop};
use proc_macro2::{LineColumn, Span};
use quote::ToTokens;
use std::{
Expand Down Expand Up @@ -132,6 +132,39 @@ impl<'a> Writer<'a> {
Ok(())
}

pub(crate) fn attr_value_len(&mut self, value: &ElementAttrValue) -> usize {
match value {
ElementAttrValue::AttrOptionalExpr { condition, value } => {
let condition_len = self.retrieve_formatted_expr(condition).len();
let value_len = self.attr_value_len(value);

condition_len + value_len + 6
}
ElementAttrValue::AttrLiteral(lit) => ifmt_to_string(lit).len(),
ElementAttrValue::AttrExpr(expr) => expr.span().line_length(),
ElementAttrValue::EventTokens(tokens) => {
let location = Location::new(tokens.span().start());

let len = if let std::collections::hash_map::Entry::Vacant(e) =
self.cached_formats.entry(location)
{
let formatted = prettyplease::unparse_expr(tokens);
let len = if formatted.contains('\n') {
10000
} else {
formatted.len()
};
e.insert(formatted);
len
} else {
self.cached_formats[&location].len()
};

len
}
}
}

pub(crate) fn is_short_attrs(&mut self, attributes: &[ElementAttrNamed]) -> usize {
let mut total = 0;

Expand All @@ -146,40 +179,17 @@ impl<'a> Writer<'a> {
}
}

total += match &attr.attr {
ElementAttr::AttrText { value, name } => {
ifmt_to_string(value).len() + name.span().line_length() + 6
}
ElementAttr::AttrExpression { name, value } => {
value.span().line_length() + name.span().line_length() + 6
}
ElementAttr::CustomAttrText { value, name } => {
ifmt_to_string(value).len() + name.to_token_stream().to_string().len() + 6
}
ElementAttr::CustomAttrExpression { name, value } => {
name.to_token_stream().to_string().len() + value.span().line_length() + 6
}
ElementAttr::EventTokens { tokens, name } => {
let location = Location::new(tokens.span().start());

let len = if let std::collections::hash_map::Entry::Vacant(e) =
self.cached_formats.entry(location)
{
let formatted = prettyplease::unparse_expr(tokens);
let len = if formatted.contains('\n') {
10000
} else {
formatted.len()
};
e.insert(formatted);
len
} else {
self.cached_formats[&location].len()
};

len + name.span().line_length() + 6
total += match &attr.attr.name {
dioxus_rsx::ElementAttrName::BuiltIn(name) => {
let name = name.to_string();
name.len()
}
dioxus_rsx::ElementAttrName::Custom(name) => name.value().len() + 2,
};

total += self.attr_value_len(&attr.attr.value);

total += 6;
}

total
Expand Down Expand Up @@ -218,7 +228,7 @@ impl<'a> Writer<'a> {
}
}

trait SpanLength {
pub(crate) trait SpanLength {
fn line_length(&self) -> usize;
}
impl SpanLength for Span {
Expand Down
2 changes: 1 addition & 1 deletion packages/autofmt/tests/samples/simple.rsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ rsx! {
}

// No children, minimal props
img { class: "mb-6 mx-auto h-24", src: "artemis-assets/images/friends.png", alt: "" }
img { class: "mb-6 mx-auto h-24", src: "artemis-assets/images/friends.png" }

// One level compression
div {
Expand Down
5 changes: 4 additions & 1 deletion packages/core/tests/kitchen_sink.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ fn basic_syntax_is_a_template(cx: Scope) -> Element {
div { key: "12345",
class: "asd",
class: "{asd}",
class: if true { "{asd}" },
class: if false { "{asd}" },
onclick: move |_| {},
div { "{var}" }
div {
Expand All @@ -24,6 +26,7 @@ fn basic_syntax_is_a_template(cx: Scope) -> Element {
}
})
}

#[test]
fn dual_stream() {
let mut dom = VirtualDom::new(basic_syntax_is_a_template);
Expand All @@ -36,7 +39,7 @@ fn dual_stream() {
LoadTemplate { name: "template", index: 0, id: ElementId(1) },
SetAttribute {
name: "class",
value: (&*bump.alloc("123".into_value(&bump))).into(),
value: (&*bump.alloc("asd 123 123".into_value(&bump))).into(),
id: ElementId(1),
ns: None,
},
Expand Down
25 changes: 19 additions & 6 deletions packages/rsx-rosetta/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,12 @@ pub fn rsx_node_from_html(node: &Node) -> Option<BodyNode> {

ElementAttrNamed {
el_name: el_name.clone(),
// attr: ElementAttr {
// value: dioxus_rsx::ElementAttrValue::AttrLiteral(ifmt_from_text(
// value.as_deref().unwrap_or("false"),
// )),
// name: dioxus_rsx::ElementAttrName::BuiltIn(ident),
// },
attr,
}
})
Expand All @@ -68,19 +74,25 @@ pub fn rsx_node_from_html(node: &Node) -> Option<BodyNode> {
if !class.is_empty() {
attributes.push(ElementAttrNamed {
el_name: el_name.clone(),
attr: ElementAttr::AttrText {
name: Ident::new("class", Span::call_site()),
value: ifmt_from_text(&class),
attr: ElementAttr {
name: dioxus_rsx::ElementAttrName::BuiltIn(Ident::new(
"class",
Span::call_site(),
)),
value: dioxus_rsx::ElementAttrValue::AttrLiteral(ifmt_from_text(&class)),
},
});
}

if let Some(id) = &el.id {
attributes.push(ElementAttrNamed {
el_name: el_name.clone(),
attr: ElementAttr::AttrText {
name: Ident::new("id", Span::call_site()),
value: ifmt_from_text(id),
attr: ElementAttr {
name: dioxus_rsx::ElementAttrName::BuiltIn(Ident::new(
"id",
Span::call_site(),
)),
value: dioxus_rsx::ElementAttrValue::AttrLiteral(ifmt_from_text(id)),
},
});
}
Expand All @@ -91,6 +103,7 @@ pub fn rsx_node_from_html(node: &Node) -> Option<BodyNode> {
name: el_name,
children,
attributes,
merged_attributes: Default::default(),
key: None,
brace: Default::default(),
}))
Expand Down
2 changes: 2 additions & 0 deletions packages/rsx/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,7 @@ krates = { version = "0.12.6", optional = true }
tracing.workspace = true

[features]
default = ["html"]
hot_reload = ["krates", "internment"]
serde = ["dep:serde"]
html = []
Loading

0 comments on commit d933291

Please sign in to comment.