mirror of
https://github.com/DioxusLabs/dioxus
synced 2024-11-26 22:20:19 +00:00
wip: refactor out translation into its own crate
This commit is contained in:
parent
4aac2cdae1
commit
14bc007c15
9 changed files with 153 additions and 2 deletions
|
@ -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"
|
||||||
|
|
|
@ -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();
|
||||||
|
|
20
packages/html-to-rsx/Cargo.toml
Normal file
20
packages/html-to-rsx/Cargo.toml
Normal 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
|
19
packages/html-to-rsx/README.md
Normal file
19
packages/html-to-rsx/README.md
Normal 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.
|
13
packages/html-to-rsx/examples/html.rs
Normal file
13
packages/html-to-rsx/examples/html.rs
Normal 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);
|
||||||
|
}
|
31
packages/html-to-rsx/src/lib.rs
Normal file
31
packages/html-to-rsx/src/lib.rs
Normal 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)
|
||||||
|
}
|
24
packages/rsx/src/comments.rs
Normal file
24
packages/rsx/src/comments.rs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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(_)
|
||||||
|
|
|
@ -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! {
|
||||||
|
|
Loading…
Reference in a new issue