Skip to content

Commit 5ae230e

Browse files
committed
Rust implementation of "rescript format"
1 parent 9454207 commit 5ae230e

File tree

8 files changed

+159
-12
lines changed

8 files changed

+159
-12
lines changed

rewatch/Cargo.lock

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

rewatch/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ log = { version = "0.4.17" }
2020
notify = { version = "5.1.0", features = ["serde"] }
2121
notify-debouncer-mini = { version = "0.2.0" }
2222
rayon = "1.6.1"
23+
num_cpus = "1.17.0"
2324
regex = "1.7.1"
2425
serde = { version = "1.0.152", features = ["derive"] }
2526
serde_derive = "1.0.152"

rewatch/src/cli.rs

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -144,11 +144,19 @@ pub enum Command {
144144
#[command(flatten)]
145145
snapshot_output: SnapshotOutputArg,
146146
},
147-
/// Alias to `legacy format`.
148-
#[command(disable_help_flag = true)]
147+
/// Formats ReScript files.
149148
Format {
150-
#[arg(allow_hyphen_values = true, num_args = 0..)]
151-
format_args: Vec<OsString>,
149+
/// Read the code from stdin and print the formatted code to stdout.
150+
#[arg(long)]
151+
stdin: Option<String>,
152+
/// Format the whole project.
153+
#[arg(short = 'a', long)]
154+
all: bool,
155+
/// Check formatting for file or the whole project. Use `--all` to check the whole project.
156+
#[arg(short = 'c', long)]
157+
check: bool,
158+
/// Files to format.
159+
files: Vec<String>,
152160
},
153161
/// Alias to `legacy dump`.
154162
#[command(disable_help_flag = true)]

rewatch/src/format_cmd.rs

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
use crate::helpers;
2+
use anyhow::{Result, bail};
3+
use num_cpus;
4+
use rayon::prelude::*;
5+
use std::fs;
6+
use std::io::{self, Read, Write};
7+
use std::path::Path;
8+
use std::process::Command;
9+
use std::sync::atomic::{AtomicUsize, Ordering};
10+
11+
pub fn run(stdin_path: Option<String>, all: bool, check: bool, files: Vec<String>) -> Result<()> {
12+
let bsc_path = helpers::get_bsc();
13+
14+
if check && stdin_path.is_some() {
15+
bail!("format --stdin cannot be used with --check flag");
16+
}
17+
18+
if all {
19+
if stdin_path.is_some() || !files.is_empty() {
20+
bail!("format --all can not be in use with other flags");
21+
}
22+
format_all(&bsc_path, check)?;
23+
} else if stdin_path.is_some() {
24+
format_stdin(&bsc_path, stdin_path.unwrap())?;
25+
} else {
26+
format_files(&bsc_path, files, check)?;
27+
}
28+
29+
Ok(())
30+
}
31+
32+
fn format_all(bsc_exe: &Path, check: bool) -> Result<()> {
33+
let output = Command::new(std::env::current_exe()?)
34+
.arg("info")
35+
.arg("-list-files")
36+
.output()?;
37+
38+
if !output.status.success() {
39+
io::stderr().write_all(&output.stderr)?;
40+
bail!("Failed to list files");
41+
}
42+
43+
let files_str = String::from_utf8_lossy(&output.stdout);
44+
let files: Vec<String> = files_str
45+
.split('\n')
46+
.filter(|s| !s.trim().is_empty())
47+
.map(|s| s.trim().to_string())
48+
.collect();
49+
50+
format_files(bsc_exe, files, check)?;
51+
Ok(())
52+
}
53+
54+
fn format_stdin(bsc_exe: &Path, stdin_path: String) -> Result<()> {
55+
let mut input = String::new();
56+
io::stdin().read_to_string(&mut input)?;
57+
58+
let mut cmd = Command::new(bsc_exe);
59+
cmd.arg("-format").arg(&stdin_path);
60+
cmd.stdin(std::process::Stdio::piped());
61+
cmd.stdout(std::process::Stdio::piped());
62+
cmd.stderr(std::process::Stdio::piped());
63+
64+
let mut child = cmd.spawn()?;
65+
let mut stdin = child.stdin.take().unwrap();
66+
std::thread::spawn(move || {
67+
stdin.write_all(input.as_bytes()).unwrap();
68+
});
69+
70+
let output = child.wait_with_output()?;
71+
72+
if output.status.success() {
73+
io::stdout().write_all(&output.stdout)?;
74+
} else {
75+
io::stderr().write_all(&output.stderr)?;
76+
bail!("bsc exited with an error");
77+
}
78+
79+
Ok(())
80+
}
81+
82+
fn format_files(bsc_exe: &Path, files: Vec<String>, check: bool) -> Result<()> {
83+
let batch_size = 4 * num_cpus::get();
84+
let incorrectly_formatted_files = AtomicUsize::new(0);
85+
86+
files.par_chunks(batch_size).try_for_each(|batch| {
87+
batch.iter().try_for_each(|file| {
88+
let mut cmd = Command::new(bsc_exe);
89+
if check {
90+
cmd.arg("-format").arg(file);
91+
} else {
92+
cmd.arg("-o").arg(file).arg("-format").arg(file);
93+
}
94+
95+
let output = cmd.output()?;
96+
97+
if output.status.success() {
98+
if check {
99+
let original_content = fs::read_to_string(file)?;
100+
let formatted_content = String::from_utf8_lossy(&output.stdout);
101+
if original_content != formatted_content {
102+
eprintln!("[format check] {}", file);
103+
incorrectly_formatted_files.fetch_add(1, Ordering::SeqCst);
104+
}
105+
}
106+
} else {
107+
io::stderr().write_all(&output.stderr)?;
108+
bail!("bsc exited with an error for file {}", file);
109+
}
110+
Ok(()) as Result<()>
111+
})
112+
})?;
113+
114+
let count = incorrectly_formatted_files.load(Ordering::SeqCst);
115+
if count > 0 {
116+
if count == 1 {
117+
eprintln!("The file listed above needs formatting");
118+
} else {
119+
eprintln!("The {} files listed above need formatting", count);
120+
}
121+
bail!("Formatting check failed");
122+
}
123+
124+
Ok(())
125+
}

rewatch/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ pub mod build;
22
pub mod cli;
33
pub mod cmd;
44
pub mod config;
5+
pub mod format_cmd;
56
pub mod helpers;
67
pub mod lock;
78
pub mod queue;

rewatch/src/main.rs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use log::LevelFilter;
44
use regex::Regex;
55
use std::{io::Write, path::Path};
66

7-
use rewatch::{build, cli, cmd, lock, watcher};
7+
use rewatch::{build, cli, cmd, format_cmd, lock, watcher};
88

99
fn main() -> Result<()> {
1010
let args = cli::Cli::parse();
@@ -88,11 +88,12 @@ fn main() -> Result<()> {
8888
let code = build::pass_through_legacy(legacy_args);
8989
std::process::exit(code);
9090
}
91-
cli::Command::Format { mut format_args } => {
92-
format_args.insert(0, "format".into());
93-
let code = build::pass_through_legacy(format_args);
94-
std::process::exit(code);
95-
}
91+
cli::Command::Format {
92+
stdin,
93+
all,
94+
check,
95+
files,
96+
} => format_cmd::run(stdin, all, check, files),
9697
cli::Command::Dump { mut dump_args } => {
9798
dump_args.insert(0, "dump".into());
9899
let code = build::pass_through_legacy(dump_args);

scripts/format.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ dune build @fmt --auto-promote
77

88
echo Formatting ReScript code...
99
files=$(find runtime tests -type f \( -name "*.res" -o -name "*.resi" \) ! -name "syntaxErrors*" ! -name "generated_mocha_test.res" ! -path "tests/syntax_tests*" ! -path "tests/analysis_tests/tests*" ! -path "*/node_modules/*")
10-
./cli/rescript-legacy.js format $files
10+
./cli/rescript.js format $files
1111

1212
echo Formatting JS code...
1313
yarn format

scripts/format_check.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ case "$(uname -s)" in
1818

1919
echo "Checking ReScript code formatting..."
2020
files=$(find runtime tests -type f \( -name "*.res" -o -name "*.resi" \) ! -name "syntaxErrors*" ! -name "generated_mocha_test.res" ! -path "tests/syntax_tests*" ! -path "tests/analysis_tests/tests*" ! -path "*/node_modules/*")
21-
if ./cli/rescript-legacy.js format -check $files; then
21+
if ./cli/rescript.js format --check $files; then
2222
printf "${successGreen}✅ ReScript code formatting ok.${reset}\n"
2323
else
2424
printf "${warningYellow}⚠️ ReScript code formatting issues found. Run 'make format' to fix.${reset}\n"

0 commit comments

Comments
 (0)