diff --git a/leptos_hot_reload/Cargo.toml b/leptos_hot_reload/Cargo.toml index afaed4667c..afa193fd09 100644 --- a/leptos_hot_reload/Cargo.toml +++ b/leptos_hot_reload/Cargo.toml @@ -20,7 +20,7 @@ syn = { version = "2.0", features = [ "printing", ] } quote = "1.0" -rstml = "0.11.2" +rstml = "0.12.0" proc-macro2 = { version = "1.0", features = ["span-locations", "nightly"] } parking_lot = "0.12.3" walkdir = "2.5" diff --git a/leptos_hot_reload/src/parsing.rs b/leptos_hot_reload/src/parsing.rs index 697b0d5387..e14f80196b 100644 --- a/leptos_hot_reload/src/parsing.rs +++ b/leptos_hot_reload/src/parsing.rs @@ -1,4 +1,4 @@ -use rstml::node::{NodeElement, NodeName}; +use rstml::node::{CustomNode, NodeElement, NodeName}; /// Converts `syn::Block` to simple expression /// @@ -65,6 +65,6 @@ pub fn is_component_tag_name(name: &NodeName) -> bool { } #[must_use] -pub fn is_component_node(node: &NodeElement) -> bool { +pub fn is_component_node(node: &NodeElement) -> bool { is_component_tag_name(node.name()) } diff --git a/leptos_macro/Cargo.toml b/leptos_macro/Cargo.toml index c96ecc3d87..fdba5e3d54 100644 --- a/leptos_macro/Cargo.toml +++ b/leptos_macro/Cargo.toml @@ -22,7 +22,7 @@ proc-macro-error = { version = "1.0", default-features = false } proc-macro2 = "1.0" quote = "1.0" syn = { version = "2.0", features = ["full"] } -rstml = "0.11.2" +rstml = "0.12.0" leptos_hot_reload = { workspace = true } server_fn_macro = { workspace = true } convert_case = "0.6.0" diff --git a/leptos_macro/src/lib.rs b/leptos_macro/src/lib.rs index c0f7b4b444..392f4b36ca 100644 --- a/leptos_macro/src/lib.rs +++ b/leptos_macro/src/lib.rs @@ -74,6 +74,9 @@ mod slot; /// Attributes can take a wide variety of primitive types that can be converted to strings. They can also /// take an `Option`, in which case `Some` sets the attribute and `None` removes the attribute. /// +/// Note that in some cases, rust-analyzer support may be better if attribute values are surrounded with braces (`{}`). +/// Unlike in JSX, attribute values are not required to be in braces, but braces can be used and may improve this LSP support. +/// /// ```rust,ignore /// # use leptos::prelude::*; /// @@ -306,10 +309,17 @@ pub fn view(tokens: TokenStream) -> TokenStream { global_class.as_ref(), normalized_call_site(proc_macro::Span::call_site()), ); + + // The allow lint needs to be put here instead of at the expansion of + // view::attribute_value(). Adding this next to the expanded expression + // seems to break rust-analyzer, but it works when the allow is put here. quote! { { - #(#errors;)* - #nodes_output + #[allow(unused_braces)] + { + #(#errors;)* + #nodes_output + } } } .into() diff --git a/leptos_macro/src/view/component_builder.rs b/leptos_macro/src/view/component_builder.rs index baeb02b1ad..57236221f3 100644 --- a/leptos_macro/src/view/component_builder.rs +++ b/leptos_macro/src/view/component_builder.rs @@ -3,13 +3,14 @@ use crate::view::attribute_absolute; use proc_macro2::{Ident, TokenStream, TokenTree}; use quote::{format_ident, quote, quote_spanned}; use rstml::node::{ - KeyedAttributeValue, NodeAttribute, NodeBlock, NodeElement, NodeName, + CustomNode, KeyedAttributeValue, NodeAttribute, NodeBlock, NodeElement, + NodeName, }; use std::collections::HashMap; use syn::{spanned::Spanned, Expr, ExprPath, ExprRange, RangeLimits, Stmt}; pub(crate) fn component_to_tokens( - node: &NodeElement, + node: &NodeElement, global_class: Option<&TokenTree>, ) -> TokenStream { let name = node.name(); diff --git a/leptos_macro/src/view/mod.rs b/leptos_macro/src/view/mod.rs index 6d597c0586..e4d026684d 100644 --- a/leptos_macro/src/view/mod.rs +++ b/leptos_macro/src/view/mod.rs @@ -10,8 +10,8 @@ use proc_macro2::{Ident, Span, TokenStream, TokenTree}; use proc_macro_error::abort; use quote::{quote, quote_spanned, ToTokens}; use rstml::node::{ - KeyedAttribute, Node, NodeAttribute, NodeBlock, NodeElement, NodeName, - NodeNameFragment, + CustomNode, KVAttributeValue, KeyedAttribute, Node, NodeAttribute, + NodeBlock, NodeElement, NodeName, NodeNameFragment, }; use std::collections::{HashMap, HashSet}; use syn::{ @@ -89,7 +89,7 @@ pub fn render_view( } fn element_children_to_tokens( - nodes: &[Node], + nodes: &[Node], parent_type: TagType, parent_slots: Option<&mut HashMap>>, global_class: Option<&TokenTree>, @@ -117,7 +117,7 @@ fn element_children_to_tokens( } fn fragment_to_tokens( - nodes: &[Node], + nodes: &[Node], parent_type: TagType, parent_slots: Option<&mut HashMap>>, global_class: Option<&TokenTree>, @@ -142,7 +142,7 @@ fn fragment_to_tokens( } fn children_to_tokens( - nodes: &[Node], + nodes: &[Node], parent_type: TagType, parent_slots: Option<&mut HashMap>>, global_class: Option<&TokenTree>, @@ -186,7 +186,7 @@ fn children_to_tokens( } fn node_to_tokens( - node: &Node, + node: &Node, parent_type: TagType, parent_slots: Option<&mut HashMap>>, global_class: Option<&TokenTree>, @@ -219,6 +219,7 @@ fn node_to_tokens( global_class, view_marker, ), + Node::Custom(node) => Some(node.to_token_stream()), } } @@ -236,7 +237,7 @@ fn text_to_tokens(text: &LitStr) -> TokenStream { } pub(crate) fn element_to_tokens( - node: &NodeElement, + node: &NodeElement, mut parent_type: TagType, parent_slots: Option<&mut HashMap>>, global_class: Option<&TokenTree>, @@ -411,7 +412,7 @@ pub(crate) fn element_to_tokens( } } -fn is_spread_marker(node: &NodeElement) -> bool { +fn is_spread_marker(node: &NodeElement) -> bool { match node.name() { NodeName::Block(block) => matches!( block.stmts.first(), @@ -773,7 +774,7 @@ fn is_custom_element(tag: &str) -> bool { tag.contains('-') } -fn is_self_closing(node: &NodeElement) -> bool { +fn is_self_closing(node: &NodeElement) -> bool { // self-closing tags // https://developer.mozilla.org/en-US/docs/Glossary/Empty_element [ @@ -921,20 +922,31 @@ fn attribute_name(name: &NodeName) -> TokenStream { } fn attribute_value(attr: &KeyedAttribute) -> TokenStream { - match attr.value() { - Some(value) => { - if let Expr::Lit(lit) = value { - if cfg!(feature = "nightly") { - if let Lit::Str(str) = &lit.lit { - return quote! { - ::leptos::tachys::view::static_types::Static::<#str> - }; + match attr.possible_value.to_value() { + None => quote! { true }, + Some(value) => match &value.value { + KVAttributeValue::Expr(expr) => { + if let Expr::Lit(lit) = expr { + if cfg!(feature = "nightly") { + if let Lit::Str(str) = &lit.lit { + return quote! { + ::leptos::tachys::view::static_types::Static::<#str> + }; + } } } + + quote! { + {#expr} + } } - quote! { #value } - } - None => quote! { true }, + // any value in braces: expand as-is to give proper r-a support + KVAttributeValue::InvalidBraced(block) => { + quote! { + #block + } + } + }, } } diff --git a/leptos_macro/src/view/slot_helper.rs b/leptos_macro/src/view/slot_helper.rs index 0f292a9cec..044805ced7 100644 --- a/leptos_macro/src/view/slot_helper.rs +++ b/leptos_macro/src/view/slot_helper.rs @@ -2,12 +2,12 @@ use super::{convert_to_snake_case, ident_from_tag_name}; use crate::view::{fragment_to_tokens, TagType}; use proc_macro2::{Ident, TokenStream, TokenTree}; use quote::{format_ident, quote, quote_spanned}; -use rstml::node::{KeyedAttribute, NodeAttribute, NodeElement}; +use rstml::node::{CustomNode, KeyedAttribute, NodeAttribute, NodeElement}; use std::collections::HashMap; use syn::spanned::Spanned; pub(crate) fn slot_to_tokens( - node: &NodeElement, + node: &NodeElement, slot: &KeyedAttribute, parent_slots: Option<&mut HashMap>>, global_class: Option<&TokenTree>, @@ -213,7 +213,9 @@ pub(crate) fn is_slot(node: &KeyedAttribute) -> bool { key == "slot" || key.starts_with("slot:") } -pub(crate) fn get_slot(node: &NodeElement) -> Option<&KeyedAttribute> { +pub(crate) fn get_slot( + node: &NodeElement, +) -> Option<&KeyedAttribute> { node.attributes().iter().find_map(|node| { if let NodeAttribute::Attribute(node) = node { if is_slot(node) {