瀏覽代碼

Merge pull request #1902 from Dangerised/master

add rustfmt support
Evan Almloff 1 年之前
父節點
當前提交
0a4603d30e
共有 5 個文件被更改,包括 90 次插入95 次删除
  1. 4 10
      Cargo.lock
  2. 1 1
      Cargo.toml
  3. 7 2
      packages/cli/Cargo.toml
  4. 78 81
      packages/cli/src/cli/autoformat.rs
  5. 0 1
      packages/cli/src/main.rs

+ 4 - 10
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"

+ 1 - 1
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"

+ 7 - 2
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 = []

+ 78 - 81
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<PathBuf> {
-    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<PathBuf> {
+    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<Path>,
     indent: IndentOptions,
-) -> Result<usize, tokio::io::Error> {
-    let contents = tokio::fs::read_to_string(&path).await?;
+    format_rust_code: bool,
+) -> Result<usize> {
+    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::<FuturesUnordered<_>>()
-        .collect::<Vec<_>>()
-        .await;
+        .collect::<Vec<_>>();
 
     let files_formatted: usize = counts.into_iter().flatten().sum();
 
@@ -242,28 +245,21 @@ fn indentation_for(
     ))
 }
 
-fn collect_rs_files(folder: &impl AsRef<Path>, files: &mut Vec<PathBuf>) {
-    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<String> {
+    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]

+ 0 - 1
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