Skip to content

Commit 8ba7e28

Browse files
authored
Merge pull request #99 from rhaiscript:cli/feat-fmt
Formattin and config in CLI
2 parents 7e85ad2 + bf982e6 commit 8ba7e28

File tree

20 files changed

+616
-43
lines changed

20 files changed

+616
-43
lines changed

crates/rhai-cli/Cargo.toml

+11-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,17 @@ rhai = "1.8.0"
1313
anyhow = "1.0.59"
1414
async-ctrlc = { version = "1.2.0", features = ["stream"] }
1515
tracing = "0.1.36"
16-
rhai-fmt = { version = "0.1.0", path = "../rhai-fmt" }
16+
rhai-fmt = { version = "0.1.0", path = "../rhai-fmt", features = ["schema"] }
17+
schemars = "0.8.10"
18+
toml = "0.5.9"
19+
figment = "0.10.6"
20+
serde_json = "1.0.85"
21+
codespan-reporting = "0.11.1"
22+
rhai-rowan = { version = "0.1.0", path = "../rhai-rowan" }
23+
itertools = "0.10.3"
24+
glob = "0.3.0"
25+
rhai-hir = { version = "0.1.0", path = "../rhai-hir" }
26+
url = "2.2.2"
1727

1828
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
1929
atty = "0.2.14"

crates/rhai-cli/src/args.rs

+45-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use std::path::PathBuf;
2+
13
use clap::{crate_version, ArgEnum, Parser, Subcommand};
24

35
#[derive(Clone, Parser)]
@@ -13,6 +15,9 @@ pub struct RhaiArgs {
1315
/// Enable logging spans.
1416
#[clap(long, global = true)]
1517
pub log_spans: bool,
18+
/// Path to `Rhai.toml` configuration file.
19+
#[clap(long, global = true)]
20+
pub config: Option<PathBuf>,
1621
#[clap(subcommand)]
1722
pub cmd: RootCommand,
1823
}
@@ -24,6 +29,15 @@ pub enum RootCommand {
2429
#[clap(subcommand)]
2530
cmd: LspCommand,
2631
},
32+
/// Configuration file operations
33+
#[clap(visible_aliases = &["cfg"])]
34+
Config {
35+
#[clap(subcommand)]
36+
cmd: ConfigCommand,
37+
},
38+
/// Format Rhai source code.
39+
#[clap(visible_aliases = &["format"])]
40+
Fmt(FmtCommand),
2741
}
2842

2943
#[derive(Clone, Subcommand)]
@@ -35,7 +49,37 @@ pub enum LspCommand {
3549
address: String,
3650
},
3751
/// Run the language server over the standard input and output.
38-
Stdio {},
52+
Stdio,
53+
}
54+
55+
#[derive(Clone, Subcommand)]
56+
pub enum ConfigCommand {
57+
/// Print the configuration JSON schema.
58+
Schema,
59+
/// Create a new configuration file with default values.
60+
Init {
61+
/// Output file path.
62+
#[clap(short = 'o', long, default_value = "Rhai.toml")]
63+
output: String,
64+
},
65+
}
66+
67+
#[derive(Clone, Parser)]
68+
pub struct FmtCommand {
69+
/// Proceed with formatting even if the files contain
70+
/// syntax errors.
71+
#[clap(short, long)]
72+
pub force: bool,
73+
74+
/// Dry-run and report any files that are not correctly formatted.
75+
#[clap(long)]
76+
pub check: bool,
77+
78+
/// Optional pattern to search for files.
79+
///
80+
/// If not provided, it will be determined by
81+
/// the configuration.
82+
pub files: Option<String>,
3983
}
4084

4185
#[derive(Clone, Copy, ArgEnum)]

crates/rhai-cli/src/execute/config.rs

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
use std::path::PathBuf;
2+
3+
use anyhow::Context;
4+
use rhai_common::{config::Config, environment::Environment};
5+
use tokio::io::AsyncWriteExt;
6+
7+
use crate::{args::ConfigCommand, Rhai};
8+
9+
impl<E: Environment> Rhai<E> {
10+
pub async fn execute_config(&self, cmd: ConfigCommand) -> Result<(), anyhow::Error> {
11+
let mut stdout = self.env.stdout();
12+
13+
match cmd {
14+
ConfigCommand::Schema => {
15+
stdout
16+
.write_all(&serde_json::to_vec(&schemars::schema_for!(Config))?)
17+
.await?;
18+
}
19+
ConfigCommand::Init { output } => {
20+
let mut p = PathBuf::from(output);
21+
22+
if !self.env.is_absolute(&p) {
23+
p = self.env.cwd().context("invalid working directory")?.join(p);
24+
}
25+
26+
if self.env.read_file(&p).await.is_ok() {
27+
tracing::info!("already initialized");
28+
return Ok(());
29+
}
30+
31+
self.env
32+
.write_file(
33+
&p,
34+
toml::to_string_pretty(&Config::default())
35+
.unwrap()
36+
.as_bytes(),
37+
)
38+
.await?;
39+
}
40+
}
41+
42+
Ok(())
43+
}
44+
}

crates/rhai-cli/src/execute/fmt.rs

+91
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
use std::path::{Path, PathBuf};
2+
3+
use anyhow::{anyhow, Context};
4+
use codespan_reporting::files::SimpleFile;
5+
use rhai_common::{environment::Environment, util::Normalize};
6+
use rhai_fmt::format_syntax;
7+
8+
use crate::{args::FmtCommand, Rhai};
9+
10+
impl<E: Environment> Rhai<E> {
11+
pub async fn execute_fmt(&mut self, cmd: FmtCommand) -> Result<(), anyhow::Error> {
12+
let cwd = self
13+
.env
14+
.cwd()
15+
.context("invalid working directory")?
16+
.normalize();
17+
18+
let hir = self.load_hir(&cwd).await?;
19+
20+
if let Some(mut files) = cmd.files {
21+
if self.env.is_dir(Path::new(&files)) {
22+
files = PathBuf::from(files)
23+
.join("**/*.rhai")
24+
.normalize()
25+
.to_string_lossy()
26+
.into();
27+
}
28+
29+
self.config.source.include = Some(vec![files]);
30+
self.config.source.exclude = None;
31+
}
32+
33+
self.config.prepare(
34+
&self.env,
35+
&self.env.cwd().context("invalid working directory")?,
36+
)?;
37+
38+
let files = self.collect_files(&cwd, &self.config, true).await?;
39+
40+
let mut result = Ok(());
41+
42+
let mut format_opts = rhai_fmt::Options::default();
43+
format_opts.update(self.config.fmt.options.clone());
44+
45+
for path in files {
46+
let f = self.env.read_file(&path).await?;
47+
let source = String::from_utf8_lossy(&f).into_owned();
48+
49+
let parser = rhai_rowan::Parser::new(&source).with_operators(hir.parser_operators());
50+
51+
let p = if rhai_rowan::util::is_rhai_def(&source) {
52+
parser.parse_def()
53+
} else {
54+
parser.parse_script()
55+
};
56+
57+
if !p.errors.is_empty() {
58+
self.print_parse_errors(
59+
&SimpleFile::new(&*path.to_string_lossy(), source.as_str()),
60+
&p.errors,
61+
)
62+
.await?;
63+
64+
if !cmd.force {
65+
if cmd.check {
66+
result = Err(anyhow!("some files had syntax errors"));
67+
} else {
68+
result = Err(anyhow!(
69+
"some files were not formatted due to syntax errors"
70+
));
71+
}
72+
73+
continue;
74+
}
75+
}
76+
77+
let formatted = format_syntax(p.into_syntax(), format_opts.clone());
78+
79+
if source != formatted {
80+
if cmd.check {
81+
tracing::error!(?path, "the file is not properly formatted");
82+
result = Err(anyhow!("some files were not properly formatted"));
83+
} else {
84+
self.env.write_file(&path, formatted.as_bytes()).await?;
85+
}
86+
}
87+
}
88+
89+
result
90+
}
91+
}

crates/rhai-cli/src/execute/mod.rs

+15-1
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,29 @@
11
use crate::{
2-
args::{RhaiArgs, RootCommand},
2+
args::{Colors, RhaiArgs, RootCommand},
33
Rhai,
44
};
55
use rhai_common::environment::Environment;
66

7+
mod config;
8+
mod fmt;
79
mod lsp;
810

911
impl<E: Environment> Rhai<E> {
1012
pub async fn execute(&mut self, args: RhaiArgs) -> Result<(), anyhow::Error> {
13+
if let RootCommand::Fmt(_) = &args.cmd {
14+
self.load_config(&args).await?
15+
}
16+
17+
self.colors = match args.colors {
18+
Colors::Auto => self.env.atty_stderr(),
19+
Colors::Always => true,
20+
Colors::Never => false,
21+
};
22+
1123
match args.cmd {
1224
RootCommand::Lsp { cmd } => self.execute_lsp(cmd).await,
25+
RootCommand::Config { cmd } => self.execute_config(cmd).await,
26+
RootCommand::Fmt(cmd) => self.execute_fmt(cmd).await,
1327
}
1428
}
1529
}

0 commit comments

Comments
 (0)