Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add rustfmt support #1902

Merged
merged 11 commits into from
Feb 9, 2024
12 changes: 2 additions & 10 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

31 changes: 15 additions & 16 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -58,23 +58,23 @@ version = "0.4.3"
[workspace.dependencies]
dioxus = { path = "packages/dioxus", version = "0.4.0" }
dioxus-core = { path = "packages/core", version = "0.4.2" }
dioxus-core-macro = { path = "packages/core-macro", version = "0.4.0" }
dioxus-router = { path = "packages/router", version = "0.4.1" }
dioxus-core-macro = { path = "packages/core-macro", version = "0.4.0" }
dioxus-router = { path = "packages/router", version = "0.4.1" }
dioxus-router-macro = { path = "packages/router-macro", version = "0.4.1" }
dioxus-html = { path = "packages/html", default-features = false, version = "0.4.0" }
dioxus-html-internal-macro = { path = "packages/html-internal-macro", version = "0.4.0" }
dioxus-html = { path = "packages/html", default-features = false, version = "0.4.0" }
dioxus-html-internal-macro = { path = "packages/html-internal-macro", version = "0.4.0" }
dioxus-hooks = { path = "packages/hooks", version = "0.4.0" }
dioxus-web = { path = "packages/web", version = "0.4.0" }
dioxus-ssr = { path = "packages/ssr", version = "0.4.0" }
dioxus-desktop = { path = "packages/desktop", version = "0.4.0" }
dioxus-mobile = { path = "packages/mobile", version = "0.4.0" }
dioxus-web = { path = "packages/web", version = "0.4.0" }
dioxus-ssr = { path = "packages/ssr", version = "0.4.0" }
dioxus-desktop = { path = "packages/desktop", version = "0.4.0" }
dioxus-mobile = { path = "packages/mobile", version = "0.4.0" }
dioxus-interpreter-js = { path = "packages/interpreter", version = "0.4.0" }
fermi = { path = "packages/fermi", version = "0.4.0" }
dioxus-liveview = { path = "packages/liveview", version = "0.4.0" }
dioxus-autofmt = { path = "packages/autofmt", version = "0.4.0" }
dioxus-check = { path = "packages/check", version = "0.4.0" }
dioxus-rsx = { path = "packages/rsx", version = "0.4.0" }
dioxus-tui = { path = "packages/dioxus-tui", version = "0.4.0" }
fermi = { path = "packages/fermi", version = "0.4.0" }
dioxus-liveview = { path = "packages/liveview", version = "0.4.0" }
dioxus-autofmt = { path = "packages/autofmt", version = "0.4.0" }
dioxus-check = { path = "packages/check", version = "0.4.0" }
dioxus-rsx = { path = "packages/rsx", version = "0.4.0" }
dioxus-tui = { path = "packages/dioxus-tui", version = "0.4.0" }
plasmo = { path = "packages/rink", version = "0.4.0" }
dioxus-native-core = { path = "packages/native-core", version = "0.4.0" }
dioxus-native-core-macro = { path = "packages/native-core-macro", version = "0.4.0" }
Expand All @@ -83,7 +83,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"
Expand Down Expand Up @@ -144,4 +144,3 @@ thiserror = { workspace = true }
manganis = { workspace = true }
tracing-subscriber = "0.3.17"
http-range = "0.1.5"

7 changes: 7 additions & 0 deletions packages/autofmt/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -240,3 +240,10 @@ pub(crate) fn write_ifmt(input: &IfmtInput, writable: &mut impl Write) -> std::f
let display = DisplayIfmt(input);
write!(writable, "{}", display)
}

/// Call rustfmt to format code, i32 as exitcode
pub fn rustfmt(input: &str) -> Option<String> {
let syntax_tree = syn::parse_file(input).ok()?;
let output = prettyplease::unparse(&syntax_tree);
Some(output)
}
5 changes: 3 additions & 2 deletions packages/cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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"] }
Expand Down Expand Up @@ -84,7 +85,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 = []
Expand Down
135 changes: 54 additions & 81 deletions packages/cli/src/cli/autoformat.rs
Original file line number Diff line number Diff line change
@@ -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::*;
Expand All @@ -10,6 +10,10 @@ use super::*;
/// Format some rsx
#[derive(Clone, Debug, Parser)]
pub struct Autoformat {
/// Run rustfmt before the dioxus formatter`
#[clap(long)]
pub rustfmt: 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)]
Expand All @@ -36,12 +40,13 @@ impl Autoformat {
raw,
file,
split_line_attributes,
rustfmt: do_rustfmt,
..
} = 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, do_rustfmt).await {
eprintln!("error formatting project: {}", e);
exit(1);
}
Expand All @@ -60,14 +65,14 @@ impl Autoformat {

// Format single file
if let Some(file) = file {
refactor_file(file, split_line_attributes)?;
refactor_file(file, split_line_attributes, do_rustfmt)?;
}

Ok(())
}
}

fn refactor_file(file: String, split_line_attributes: bool) -> Result<(), Error> {
fn refactor_file(file: String, split_line_attributes: bool, do_rustfmt: bool) -> Result<(), Error> {
let indent = indentation_for(".", split_line_attributes)?;
let file_content = if file == "-" {
let mut contents = String::new();
Expand All @@ -76,10 +81,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 do_rustfmt {
s = dioxus_autofmt::rustfmt(&s).ok_or_else(|| Error::ParseError("Syntax Error".into()))?;
}

let edits = dioxus_autofmt::fmt_file(&s, indent);
let out = dioxus_autofmt::apply_formats(&s, edits);

Expand All @@ -94,50 +104,42 @@ 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
fn format_file(path: impl AsRef<Path>, indent: IndentOptions, do_rustfmt: bool) -> Result<usize> {
let mut contents = fs::read_to_string(&path)?;
let mut if_write = false;
if do_rustfmt {
let formatted = dioxus_autofmt::rustfmt(&contents)
.ok_or_else(|| Error::ParseError("Syntax Error".into()))?;
if contents != formatted {
if_write = true;
contents = formatted;
}
}
}

async fn format_file(
path: impl AsRef<Path>,
indent: IndentOptions,
) -> Result<usize, tokio::io::Error> {
let contents = tokio::fs::read_to_string(&path).await?;

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)
Expand All @@ -147,11 +149,13 @@ async fn format_file(
///
/// Runs using Tokio 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);
/// Doesnhttps://www.rustwiki.org.cn/zh-CN/std/'t do mod-descending, so it will still try to format unreachable files. TODO.
ealmloff marked this conversation as resolved.
Show resolved Hide resolved
async fn autoformat_project(
check: bool,
split_line_attributes: bool,
do_rustfmt: bool,
) -> Result<()> {
let files_to_format = get_project_files();

if files_to_format.is_empty() {
return Ok(());
Expand All @@ -160,26 +164,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(), do_rustfmt);
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();

Expand Down Expand Up @@ -238,30 +234,6 @@ 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);
}
}
}
}

#[tokio::test]
async fn test_auto_fmt() {
let test_rsx = r#"
Expand All @@ -280,6 +252,7 @@ async fn test_auto_fmt() {
.to_string();

let fmt = Autoformat {
rustfmt: false,
check: false,
raw: Some(test_rsx),
file: None,
Expand Down
3 changes: 3 additions & 0 deletions packages/cli/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ pub enum Error {

#[error(transparent)]
Other(#[from] anyhow::Error),

#[error("Child process exited with {0}")]
ealmloff marked this conversation as resolved.
Show resolved Hide resolved
ExitCodeNotZero(i32),
}

impl From<&str> for Error {
Expand Down