diff --git a/Cargo.lock b/Cargo.lock index e94386bd5..3477f46ca 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2405,11 +2405,11 @@ dependencies = [ "flate2", "fs_extra", "futures-util", - "gitignore", "headers 0.3.9", "html_parser", "hyper 0.14.28", "hyper-rustls 0.23.2", + "ignore", "indicatif", "interprocess-docfix", "lazy_static", @@ -2418,11 +2418,14 @@ dependencies = [ "mlua", "notify", "open", + "prettier-please", + "rayon", "reqwest", "rsx-rosetta", "serde", "serde_json", "subprocess", + "syn 2.0.48", "tar", "tauri-bundler", "tempfile", @@ -3917,15 +3920,6 @@ dependencies = [ "url", ] -[[package]] -name = "gitignore" -version = "1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d051488d9a601181a9b90c9ad8ae7e8251d642ddd2463008f2f5019d255bd89" -dependencies = [ - "glob", -] - [[package]] name = "gix-actor" version = "0.28.1" diff --git a/Cargo.toml b/Cargo.toml index 5eaa767b9..5fcbca6e8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -84,7 +84,7 @@ dioxus-signals = { path = "packages/signals" } dioxus-cli-config = { path = "packages/cli-config", version = "0.4.1" } generational-box = { path = "packages/generational-box", version = "0.4.3" } dioxus-hot-reload = { path = "packages/hot-reload", version = "0.4.0" } -dioxus-fullstack = { path = "packages/fullstack", version = "0.4.1" } +dioxus-fullstack = { path = "packages/fullstack", version = "0.4.1" } dioxus_server_macro = { path = "packages/server-macro", version = "0.4.1" } tracing = "0.1.37" tracing-futures = "0.2.5" diff --git a/packages/cli/Cargo.toml b/packages/cli/Cargo.toml index fd8b4e2de..657c70e0f 100644 --- a/packages/cli/Cargo.toml +++ b/packages/cli/Cargo.toml @@ -26,7 +26,7 @@ fs_extra = "1.2.0" cargo_toml = "0.18.0" futures-util = { workspace = true } notify = { version = "5.0.0-pre.16", features = ["serde"] } -html_parser = { workspace = true } +html_parser = { workspace = true } cargo_metadata = "0.18.1" tokio = { version = "1.16.1", features = ["fs", "sync", "rt", "macros"] } atty = "0.2.14" @@ -36,6 +36,7 @@ hyper = "0.14.17" hyper-rustls = "0.23.2" indicatif = "0.17.5" subprocess = "0.2.9" +rayon = "1.8.0" axum = { version = "0.5.1", features = ["ws", "headers"] } axum-server = { version = "0.5.1", features = ["tls-rustls"] } @@ -74,6 +75,10 @@ toml_edit = "0.21.0" # bundling tauri-bundler = { version = "=1.4.*", features = ["native-tls-vendored"] } +# formatting +syn = { version = "2.0" } +prettyplease = { workspace = true } + manganis-cli-support = { workspace = true, features = ["webp", "html"] } dioxus-autofmt = { workspace = true } @@ -84,7 +89,7 @@ dioxus-html = { workspace = true, features = ["hot-reload-context"] } dioxus-core = { workspace = true, features = ["serialize"] } dioxus-hot-reload = { workspace = true } interprocess-docfix = { version = "1.2.2" } -gitignore = "1.0.8" +ignore = "0.4.22" [features] default = [] diff --git a/packages/cli/src/cli/autoformat.rs b/packages/cli/src/cli/autoformat.rs index 7eb631dc6..b3d6cedf9 100644 --- a/packages/cli/src/cli/autoformat.rs +++ b/packages/cli/src/cli/autoformat.rs @@ -1,5 +1,5 @@ use dioxus_autofmt::{IndentOptions, IndentType}; -use futures_util::{stream::FuturesUnordered, StreamExt}; +use rayon::prelude::*; use std::{fs, path::Path, process::exit}; use super::*; @@ -10,6 +10,10 @@ use super::*; /// Format some rsx #[derive(Clone, Debug, Parser)] pub struct Autoformat { + /// Format rust code before the formatting the rsx macros + #[clap(long)] + pub all_code: bool, + /// Run in 'check' mode. Exits with 0 if input is formatted correctly. Exits /// with 1 and prints a diff if formatting is required. #[clap(short, long)] @@ -29,19 +33,19 @@ pub struct Autoformat { } impl Autoformat { - // Todo: autoformat the entire crate - pub async fn autoformat(self) -> Result<()> { + pub fn autoformat(self) -> Result<()> { let Autoformat { check, raw, file, split_line_attributes, + all_code: format_rust_code, .. } = self; // Default to formatting the project if raw.is_none() && file.is_none() { - if let Err(e) = autoformat_project(check, split_line_attributes).await { + if let Err(e) = autoformat_project(check, split_line_attributes, format_rust_code) { eprintln!("error formatting project: {}", e); exit(1); } @@ -60,14 +64,18 @@ impl Autoformat { // Format single file if let Some(file) = file { - refactor_file(file, split_line_attributes)?; + refactor_file(file, split_line_attributes, format_rust_code)?; } Ok(()) } } -fn refactor_file(file: String, split_line_attributes: bool) -> Result<(), Error> { +fn refactor_file( + file: String, + split_line_attributes: bool, + format_rust_code: bool, +) -> Result<(), Error> { let indent = indentation_for(".", split_line_attributes)?; let file_content = if file == "-" { let mut contents = String::new(); @@ -76,10 +84,15 @@ fn refactor_file(file: String, split_line_attributes: bool) -> Result<(), Error> } else { fs::read_to_string(&file) }; - let Ok(s) = file_content else { + let Ok(mut s) = file_content else { eprintln!("failed to open file: {}", file_content.unwrap_err()); exit(1); }; + + if format_rust_code { + s = format_rust(&s)?; + } + let edits = dioxus_autofmt::fmt_file(&s, indent); let out = dioxus_autofmt::apply_formats(&s, edits); @@ -94,50 +107,46 @@ fn refactor_file(file: String, split_line_attributes: bool) -> Result<(), Error> Ok(()) } -fn get_project_files(config: &CrateConfig) -> Vec { - let mut files = vec![]; - - let gitignore_path = config.crate_dir.join(".gitignore"); - if gitignore_path.is_file() { - let gitigno = gitignore::File::new(gitignore_path.as_path()).unwrap(); - if let Ok(git_files) = gitigno.included_files() { - let git_files = git_files - .into_iter() - .filter(|f| f.ends_with(".rs") && !is_target_dir(f)); - files.extend(git_files) - }; - } else { - collect_rs_files(&config.crate_dir, &mut files); +use std::ffi::OsStr; +fn get_project_files() -> Vec { + let mut files = Vec::new(); + for result in ignore::Walk::new("./") { + let path = result.unwrap().into_path(); + if let Some(ext) = path.extension() { + if ext == OsStr::new("rs") { + files.push(path); + } + } } - files } -fn is_target_dir(file: &Path) -> bool { - let stripped = if let Ok(cwd) = std::env::current_dir() { - file.strip_prefix(cwd).unwrap_or(file) - } else { - file - }; - if let Some(first) = stripped.components().next() { - first.as_os_str() == "target" - } else { - false - } -} - -async fn format_file( +fn format_file( path: impl AsRef, indent: IndentOptions, -) -> Result { - let contents = tokio::fs::read_to_string(&path).await?; + format_rust_code: bool, +) -> Result { + let mut contents = fs::read_to_string(&path)?; + let mut if_write = false; + if format_rust_code { + let formatted = format_rust(&contents) + .map_err(|err| Error::ParseError(format!("Syntax Error:\n{}", err)))?; + if contents != formatted { + if_write = true; + contents = formatted; + } + } let edits = dioxus_autofmt::fmt_file(&contents, indent); let len = edits.len(); if !edits.is_empty() { + if_write = true; + } + + if if_write { let out = dioxus_autofmt::apply_formats(&contents, edits); - tokio::fs::write(path, out).await?; + fs::write(path, out)?; } Ok(len) @@ -145,13 +154,15 @@ async fn format_file( /// Read every .rs file accessible when considering the .gitignore and try to format it /// -/// Runs using Tokio for multithreading, so it should be really really fast +/// Runs using rayon for multithreading, so it should be really really fast /// /// Doesn't do mod-descending, so it will still try to format unreachable files. TODO. -async fn autoformat_project(check: bool, split_line_attributes: bool) -> Result<()> { - let crate_config = dioxus_cli_config::CrateConfig::new(None)?; - - let files_to_format = get_project_files(&crate_config); +fn autoformat_project( + check: bool, + split_line_attributes: bool, + format_rust_code: bool, +) -> Result<()> { + let files_to_format = get_project_files(); if files_to_format.is_empty() { return Ok(()); @@ -164,26 +175,18 @@ async fn autoformat_project(check: bool, split_line_attributes: bool) -> Result< let indent = indentation_for(&files_to_format[0], split_line_attributes)?; let counts = files_to_format - .into_iter() - .map(|path| async { - let path_clone = path.clone(); - let res = tokio::spawn(format_file(path, indent.clone())).await; - + .into_par_iter() + .map(|path| { + let res = format_file(&path, indent.clone(), format_rust_code); match res { + Ok(cnt) => Some(cnt), Err(err) => { - eprintln!("error formatting file: {}\n{err}", path_clone.display()); + eprintln!("error formatting file : {}\n{:#?}", path.display(), err); None } - Ok(Err(err)) => { - eprintln!("error formatting file: {}\n{err}", path_clone.display()); - None - } - Ok(Ok(res)) => Some(res), } }) - .collect::>() - .collect::>() - .await; + .collect::>(); let files_formatted: usize = counts.into_iter().flatten().sum(); @@ -242,28 +245,21 @@ fn indentation_for( )) } -fn collect_rs_files(folder: &impl AsRef, files: &mut Vec) { - if is_target_dir(folder.as_ref()) { - return; - } - let Ok(folder) = folder.as_ref().read_dir() else { - return; - }; - // load the gitignore - for entry in folder { - let Ok(entry) = entry else { - continue; - }; - let path = entry.path(); - if path.is_dir() { - collect_rs_files(&path, files); - } - if let Some(ext) = path.extension() { - if ext == "rs" && !is_target_dir(&path) { - files.push(path); - } - } - } +/// Format rust code using prettyplease +fn format_rust(input: &str) -> Result { + let syntax_tree = syn::parse_file(input).map_err(format_syn_error)?; + let output = prettyplease::unparse(&syntax_tree); + Ok(output) +} + +fn format_syn_error(err: syn::Error) -> Error { + let start = err.span().start(); + let line = start.line; + let column = start.column; + Error::ParseError(format!( + "Syntax Error in line {} column {}:\n{}", + line, column, err + )) } #[tokio::test] @@ -284,13 +280,14 @@ async fn test_auto_fmt() { .to_string(); let fmt = Autoformat { + all_code: false, check: false, raw: Some(test_rsx), file: None, split_line_attributes: false, }; - fmt.autoformat().await.unwrap(); + fmt.autoformat().unwrap(); } /*#[test] diff --git a/packages/cli/src/main.rs b/packages/cli/src/main.rs index 0a57735db..dae2deb2c 100644 --- a/packages/cli/src/main.rs +++ b/packages/cli/src/main.rs @@ -70,7 +70,6 @@ async fn main() -> anyhow::Result<()> { Autoformat(opts) => opts .autoformat() - .await .context(error_wrapper("Error autoformatting RSX")), Check(opts) => opts