From 14bc007c15cc6406010a940fa2aab2cfbc9d3964 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Wed, 28 Dec 2022 11:51:41 -0500 Subject: [PATCH] wip: refactor out translation into its own crate --- Cargo.toml | 1 + packages/autofmt/src/lib.rs | 28 ++++++++++++++++++++++-- packages/html-to-rsx/Cargo.toml | 20 +++++++++++++++++ packages/html-to-rsx/README.md | 19 ++++++++++++++++ packages/html-to-rsx/examples/html.rs | 13 +++++++++++ packages/html-to-rsx/src/lib.rs | 31 +++++++++++++++++++++++++++ packages/rsx/src/comments.rs | 24 +++++++++++++++++++++ packages/rsx/src/lib.rs | 3 +++ packages/rsx/src/node.rs | 16 ++++++++++++++ 9 files changed, 153 insertions(+), 2 deletions(-) create mode 100644 packages/html-to-rsx/Cargo.toml create mode 100644 packages/html-to-rsx/README.md create mode 100644 packages/html-to-rsx/examples/html.rs create mode 100644 packages/html-to-rsx/src/lib.rs create mode 100644 packages/rsx/src/comments.rs diff --git a/Cargo.toml b/Cargo.toml index 5955451f8..044421f24 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,7 @@ members = [ "packages/native-core", "packages/native-core-macro", "docs/guide", + "packages/html-to-rsx" ] # This is a "virtual package" diff --git a/packages/autofmt/src/lib.rs b/packages/autofmt/src/lib.rs index aca9f64b0..a19fe54fe 100644 --- a/packages/autofmt/src/lib.rs +++ b/packages/autofmt/src/lib.rs @@ -1,3 +1,5 @@ +use dioxus_rsx::CallBody; + use crate::buffer::*; use crate::util::*; @@ -31,6 +33,11 @@ pub struct FormattedBlock { /// Format a file into a list of `FormattedBlock`s to be applied by an IDE for autoformatting. /// /// This function expects a complete file, not just a block of code. To format individual rsx! blocks, use fmt_block instead. +/// +/// The point here is to provide precise modifications of a source file so an accompanying IDE tool can map these changes +/// back to the file precisely. +/// +/// Nested blocks of RSX will be handled automatically pub fn fmt_file(contents: &str) -> Vec { let mut formatted_blocks = Vec::new(); let mut last_bracket_end = 0; @@ -93,15 +100,32 @@ pub fn fmt_file(contents: &str) -> Vec { formatted_blocks } +pub fn write_block_out(body: CallBody) -> Option { + let mut buf = Buffer { + src: vec![], + indent: 0, + ..Buffer::default() + }; + + // Oneliner optimization + if buf.is_short_children(&body.roots).is_some() { + buf.write_ident(&body.roots[0]).unwrap(); + } else { + buf.write_body_indented(&body.roots).unwrap(); + } + + buf.consume() +} + pub fn fmt_block(block: &str, indent_level: usize) -> Option { + let body = syn::parse_str::(block).unwrap(); + let mut buf = Buffer { src: block.lines().map(|f| f.to_string()).collect(), indent: indent_level, ..Buffer::default() }; - let body = syn::parse_str::(block).unwrap(); - // Oneliner optimization if buf.is_short_children(&body.roots).is_some() { buf.write_ident(&body.roots[0]).unwrap(); diff --git a/packages/html-to-rsx/Cargo.toml b/packages/html-to-rsx/Cargo.toml new file mode 100644 index 000000000..3ff43c3b3 --- /dev/null +++ b/packages/html-to-rsx/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "rsx-rosetta" +version = "0.0.0" +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +dioxus-autofmt = { path = "../autofmt" } +dioxus-rsx = { path = "../rsx" } +html_parser = "0.6.3" +proc-macro2 = "1.0.49" +quote = "1.0.23" +syn = { version = "1.0.107", features = ["full"] } +thiserror = "1.0.38" + +# [features] +# default = ["html"] + +# eventually more output options diff --git a/packages/html-to-rsx/README.md b/packages/html-to-rsx/README.md new file mode 100644 index 000000000..35f86d00a --- /dev/null +++ b/packages/html-to-rsx/README.md @@ -0,0 +1,19 @@ +# Rosetta for RSX +--- + +Dioxus sports its own templating language inspired by C#/Kotlin/RTMP, etc. It's pretty straightforward. + +However, it's NOT HTML. This is done since HTML is verbose and you'd need a dedicated LSP or IDE integration to get a good DX in .rs files. + +RSX is simple... It's similar enough to regular Rust code to trick most IDEs into automatically providing support for things like block selections, folding, highlighting, etc. + +To accomodate the transition from HTML to RSX, you might need to translate some existing code. + +This library provids a central AST that can accept a number of inputs: + +- HTML +- Syn (todo) +- Akama (todo) +- Jinja (todo) + +From there, you can convert directly to a string or into some other AST. diff --git a/packages/html-to-rsx/examples/html.rs b/packages/html-to-rsx/examples/html.rs new file mode 100644 index 000000000..7f5031857 --- /dev/null +++ b/packages/html-to-rsx/examples/html.rs @@ -0,0 +1,13 @@ +use html_parser::Dom; + +fn main() { + let html = "hello world!"; + + let dom = Dom::parse(html).unwrap(); + + let body = rsx_rosetta::convert_from_html(dom); + + let out = dioxus_autofmt::write_block_out(body).unwrap(); + + dbg!(out); +} diff --git a/packages/html-to-rsx/src/lib.rs b/packages/html-to-rsx/src/lib.rs new file mode 100644 index 000000000..46d4017fb --- /dev/null +++ b/packages/html-to-rsx/src/lib.rs @@ -0,0 +1,31 @@ +use dioxus_rsx::{BodyNode, CallBody, IfmtInput}; +use html_parser::{Dom, Node}; +use proc_macro2::Span; +use syn::LitStr; + +#[derive(thiserror::Error, Debug)] +pub enum ConvertError {} + +pub fn convert_from_html(html: Dom) -> CallBody { + let roots = html + .children + .into_iter() + .map(|f| create_body_node_from_node(f)) + .filter_map(|f| f) + .collect(); + + CallBody { roots } +} + +fn create_body_node_from_node(node: Node) -> Option { + let res = match node { + Node::Text(text) => BodyNode::Text(IfmtInput { + source: Some(LitStr::new(text.as_str(), Span::call_site())), + segments: vec![], + }), + Node::Element(_) => todo!(), + Node::Comment(_) => return None, + }; + + Some(res) +} diff --git a/packages/rsx/src/comments.rs b/packages/rsx/src/comments.rs new file mode 100644 index 000000000..3c999eed9 --- /dev/null +++ b/packages/rsx/src/comments.rs @@ -0,0 +1,24 @@ +use std::hash::Hash; + +use proc_macro2::Span; + +// A form of whitespace +#[derive(Debug, Clone)] +pub struct UserComment { + pub span: Span, + pub comment: String, +} + +impl PartialEq for UserComment { + fn eq(&self, other: &Self) -> bool { + self.comment == other.comment + } +} + +impl Eq for UserComment {} + +impl Hash for UserComment { + fn hash(&self, state: &mut H) { + self.comment.hash(state); + } +} diff --git a/packages/rsx/src/lib.rs b/packages/rsx/src/lib.rs index d22b122e9..4b33a71c8 100644 --- a/packages/rsx/src/lib.rs +++ b/packages/rsx/src/lib.rs @@ -13,6 +13,7 @@ #[macro_use] mod errors; +mod comments; mod component; mod element; pub mod hot_reload; @@ -296,6 +297,8 @@ impl DynamicMapping { BodyNode::Text(text) if text.is_static() => {} + BodyNode::Comment(_) => {} + BodyNode::RawExpr(_) | BodyNode::Text(_) | BodyNode::ForLoop(_) diff --git a/packages/rsx/src/node.rs b/packages/rsx/src/node.rs index ef76d14ff..eb2f95a42 100644 --- a/packages/rsx/src/node.rs +++ b/packages/rsx/src/node.rs @@ -1,3 +1,5 @@ +use crate::comments::UserComment; + use super::*; use proc_macro2::{Span, TokenStream as TokenStream2}; @@ -16,6 +18,17 @@ Parse -> component() -> "text {with_args}" -> (0..10).map(|f| rsx!("asd")), // <--- notice the comma - must be a complete expr +-> // some comment here (no support for slash asterisk comments - those get deleted completely) + + + +div { + // Comment + div { // a comment here because it shares the line + + } +} + */ #[derive(PartialEq, Eq, Clone, Debug, Hash)] pub enum BodyNode { @@ -25,6 +38,7 @@ pub enum BodyNode { IfChain(ExprIf), Text(IfmtInput), RawExpr(Expr), + Comment(UserComment), } impl BodyNode { @@ -40,6 +54,7 @@ impl BodyNode { BodyNode::RawExpr(exp) => exp.span(), BodyNode::ForLoop(fl) => fl.for_token.span(), BodyNode::IfChain(f) => f.if_token.span(), + BodyNode::Comment(c) => c.span, } } } @@ -128,6 +143,7 @@ impl Parse for BodyNode { impl ToTokens for BodyNode { fn to_tokens(&self, tokens: &mut TokenStream2) { match &self { + BodyNode::Comment(_) => {} BodyNode::Element(el) => el.to_tokens(tokens), BodyNode::Component(comp) => comp.to_tokens(tokens), BodyNode::Text(txt) => tokens.append_all(quote! {