wip: refactor out translation into its own crate

This commit is contained in:
Jonathan Kelley 2022-12-28 11:51:41 -05:00
parent 4aac2cdae1
commit 14bc007c15
9 changed files with 153 additions and 2 deletions

View file

@ -19,6 +19,7 @@ members = [
"packages/native-core", "packages/native-core",
"packages/native-core-macro", "packages/native-core-macro",
"docs/guide", "docs/guide",
"packages/html-to-rsx"
] ]
# This is a "virtual package" # This is a "virtual package"

View file

@ -1,3 +1,5 @@
use dioxus_rsx::CallBody;
use crate::buffer::*; use crate::buffer::*;
use crate::util::*; 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. /// 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. /// 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<FormattedBlock> { pub fn fmt_file(contents: &str) -> Vec<FormattedBlock> {
let mut formatted_blocks = Vec::new(); let mut formatted_blocks = Vec::new();
let mut last_bracket_end = 0; let mut last_bracket_end = 0;
@ -93,15 +100,32 @@ pub fn fmt_file(contents: &str) -> Vec<FormattedBlock> {
formatted_blocks formatted_blocks
} }
pub fn write_block_out(body: CallBody) -> Option<String> {
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<String> { pub fn fmt_block(block: &str, indent_level: usize) -> Option<String> {
let body = syn::parse_str::<dioxus_rsx::CallBody>(block).unwrap();
let mut buf = Buffer { let mut buf = Buffer {
src: block.lines().map(|f| f.to_string()).collect(), src: block.lines().map(|f| f.to_string()).collect(),
indent: indent_level, indent: indent_level,
..Buffer::default() ..Buffer::default()
}; };
let body = syn::parse_str::<dioxus_rsx::CallBody>(block).unwrap();
// Oneliner optimization // Oneliner optimization
if buf.is_short_children(&body.roots).is_some() { if buf.is_short_children(&body.roots).is_some() {
buf.write_ident(&body.roots[0]).unwrap(); buf.write_ident(&body.roots[0]).unwrap();

View file

@ -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

View file

@ -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.

View file

@ -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);
}

View file

@ -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<BodyNode> {
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)
}

View file

@ -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<H: std::hash::Hasher>(&self, state: &mut H) {
self.comment.hash(state);
}
}

View file

@ -13,6 +13,7 @@
#[macro_use] #[macro_use]
mod errors; mod errors;
mod comments;
mod component; mod component;
mod element; mod element;
pub mod hot_reload; pub mod hot_reload;
@ -296,6 +297,8 @@ impl DynamicMapping {
BodyNode::Text(text) if text.is_static() => {} BodyNode::Text(text) if text.is_static() => {}
BodyNode::Comment(_) => {}
BodyNode::RawExpr(_) BodyNode::RawExpr(_)
| BodyNode::Text(_) | BodyNode::Text(_)
| BodyNode::ForLoop(_) | BodyNode::ForLoop(_)

View file

@ -1,3 +1,5 @@
use crate::comments::UserComment;
use super::*; use super::*;
use proc_macro2::{Span, TokenStream as TokenStream2}; use proc_macro2::{Span, TokenStream as TokenStream2};
@ -16,6 +18,17 @@ Parse
-> component() -> component()
-> "text {with_args}" -> "text {with_args}"
-> (0..10).map(|f| rsx!("asd")), // <--- notice the comma - must be a complete expr -> (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)] #[derive(PartialEq, Eq, Clone, Debug, Hash)]
pub enum BodyNode { pub enum BodyNode {
@ -25,6 +38,7 @@ pub enum BodyNode {
IfChain(ExprIf), IfChain(ExprIf),
Text(IfmtInput), Text(IfmtInput),
RawExpr(Expr), RawExpr(Expr),
Comment(UserComment),
} }
impl BodyNode { impl BodyNode {
@ -40,6 +54,7 @@ impl BodyNode {
BodyNode::RawExpr(exp) => exp.span(), BodyNode::RawExpr(exp) => exp.span(),
BodyNode::ForLoop(fl) => fl.for_token.span(), BodyNode::ForLoop(fl) => fl.for_token.span(),
BodyNode::IfChain(f) => f.if_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 { impl ToTokens for BodyNode {
fn to_tokens(&self, tokens: &mut TokenStream2) { fn to_tokens(&self, tokens: &mut TokenStream2) {
match &self { match &self {
BodyNode::Comment(_) => {}
BodyNode::Element(el) => el.to_tokens(tokens), BodyNode::Element(el) => el.to_tokens(tokens),
BodyNode::Component(comp) => comp.to_tokens(tokens), BodyNode::Component(comp) => comp.to_tokens(tokens),
BodyNode::Text(txt) => tokens.append_all(quote! { BodyNode::Text(txt) => tokens.append_all(quote! {