diff --git a/Cargo.toml b/Cargo.toml index 6e651cf5a..b3f31e516 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -60,6 +60,7 @@ members = [ "packages/tui", "packages/liveview", "packages/rsx", + "packages/autofmt", ] [dev-dependencies] diff --git a/packages/autofmt/Cargo.toml b/packages/autofmt/Cargo.toml index 6411f5add..2c527ed98 100644 --- a/packages/autofmt/Cargo.toml +++ b/packages/autofmt/Cargo.toml @@ -10,3 +10,5 @@ proc-macro2 = { version = "1.0.6" } quote = "1.0" syn = { version = "1.0.11", features = ["full", "extra-traits"] } dioxus-rsx = { path = "../rsx" } +triple_accel = "0.4.0" +serde = { version = "1.0.136", features = ["derive"] } diff --git a/packages/autofmt/src/lib.rs b/packages/autofmt/src/lib.rs index 9eeb82e7c..20d13afb6 100644 --- a/packages/autofmt/src/lib.rs +++ b/packages/autofmt/src/lib.rs @@ -1,9 +1,74 @@ //! pretty printer for rsx! use dioxus_rsx::*; +use proc_macro2::TokenStream as TokenStream2; use quote::ToTokens; -use std::fmt::{self, Write}; +use std::{ + fmt::{self, Write}, + ptr::NonNull, +}; +use syn::{ + buffer::TokenBuffer, + parse::{ParseBuffer, ParseStream}, +}; +use triple_accel::{levenshtein_search, Match}; + mod prettyplease; +#[derive(serde::Deserialize, serde::Serialize, Clone, Debug, PartialEq, Hash)] +pub struct ForamttedBlock { + pub formatted: String, + pub start: usize, + pub end: usize, +} + +/* +TODO: nested rsx! calls + +*/ +pub fn formmat_document(contents: &str) -> Vec { + let mut matches = levenshtein_search(b"rsx! {", contents.as_bytes()).peekable(); + + let mut cur_match: Option = None; + + let mut formatted_blocks = Vec::new(); + + while let Some(item) = matches.next() { + let Match { start, end, k } = item; + + match cur_match { + Some(ref this_match) => { + // abort nested matches - these get handled automatically + if start < this_match.end { + continue; + } else { + cur_match = Some(item); + } + } + None => { + cur_match = Some(item); + } + } + + let remaining = &contents[end - 1..]; + + if let Some(bracket_end) = find_bracket_end(remaining) { + let sub_string = &contents[end..bracket_end + end - 1]; + + if let Some(new) = fmt_block(sub_string) { + if !new.is_empty() { + formatted_blocks.push(ForamttedBlock { + formatted: new, + start: end, + end: end + bracket_end - 1, + }); + } + } + } + } + + formatted_blocks +} + pub fn fmt_block(block: &str) -> Option { let parsed: CallBody = syn::parse_str(block).ok()?; @@ -175,3 +240,24 @@ pub fn write_tabs(f: &mut dyn Write, num: usize) -> std::fmt::Result { } Ok(()) } + +fn find_bracket_end(contents: &str) -> Option { + let mut depth = 0; + let mut i = 0; + + for c in contents.chars() { + if c == '{' { + depth += 1; + } else if c == '}' { + depth -= 1; + } + + if depth == 0 { + return Some(i); + } + + i += 1; + } + + None +} diff --git a/packages/autofmt/tests/sink.rs b/packages/autofmt/tests/sink.rs index 62feeef59..8a1983b07 100644 --- a/packages/autofmt/tests/sink.rs +++ b/packages/autofmt/tests/sink.rs @@ -93,3 +93,41 @@ fn formats_component_man_props() { print!("{formatted}"); } + +#[test] +fn formats_document() { + let block = r#" +rsx!{ + Component { + adsasd: "asd", // this is a comment + onclick: move |_| { + let blah = 120; + }, + } +} + + +"#; + + let formatted = formmat_document(block); + + print!("{formatted:?}"); +} + +#[test] +fn formats_valid_rust_src() { + let src = r#" +// +rsx! { + div {} + div { + h3 {"asd" + } + } +} +"#; + + let formatted = formmat_document(src); + + println!("{formatted:?}"); +}