diff --git a/.vscode/settings.json b/.vscode/settings.json index 511da62..470946e 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,4 +1,4 @@ { - "nixEnvSelector.nixShellConfig": "${workspaceRoot}/shell.nix", - "files.trimFinalNewlines": true + "files.trimFinalNewlines": true, + "nixEnvSelector.nixFile": "${workspaceRoot}/shell.nix" } \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 6fe626b..afc901f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -610,7 +610,6 @@ dependencies = [ "structopt", "toml", "which", - "xshell", ] [[package]] @@ -700,18 +699,3 @@ name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "xshell" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed373ede30cea03e8c0af22f48ee1ba80efbf06fec8b4746977e6ee703878de0" -dependencies = [ - "xshell-macros", -] - -[[package]] -name = "xshell-macros" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f6af9f8119104697b0105989a73c578ce33f922d9d6f3dae0e8ae3d538db321" diff --git a/Cargo.toml b/Cargo.toml index ba8cd84..13ecf50 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,5 +21,4 @@ serde_json = "1.0" sha-1 = "0.9.2" structopt = "0.3" toml = "0.5" -xshell = "0.1" which = "4.0.2" diff --git a/docs/formatter_spec.md b/docs/formatter_spec.md index 01363e5..80c3af2 100644 --- a/docs/formatter_spec.md +++ b/docs/formatter_spec.md @@ -12,10 +12,11 @@ A formatter MUST adhere to the following interface: ``` Where -* `` is the name of the formatter. -* `[options]` is any number of flags and options that the formatter wants to - provide. -* `[...]` is one or more files that the formatter should process. + +- `` is the name of the formatter. +- `[options]` is any number of flags and options that the formatter wants to + provide. +- `[...]` is one or more files that the formatter should process. Whenever the program is invoked with the list of files, it MUST only process all the files that are passed and format them. Files that are not passed should never be formatted. diff --git a/docs/formatters.md b/docs/formatters.md index 965fbd3..6f49e91 100644 --- a/docs/formatters.md +++ b/docs/formatters.md @@ -1,8 +1,6 @@ # A list of known formatters - - | name | files as argument | write-in-place | update-mtime | -|-----------|-------------------|----------------|----------------------| +| --------- | ----------------- | -------------- | -------------------- | | gofmt | yes | yes | keeps the same mtime | | cargo fmt | no | yes | yes | diff --git a/examples/haskell-frontend/CHANGELOG.md b/examples/haskell-frontend/CHANGELOG.md index 500a0d0..9e57059 100644 --- a/examples/haskell-frontend/CHANGELOG.md +++ b/examples/haskell-frontend/CHANGELOG.md @@ -2,4 +2,4 @@ ## 0.1.0.0 -- YYYY-mm-dd -* First version. Released on an unsuspecting world. +- First version. Released on an unsuspecting world. diff --git a/examples/haskell/CHANGELOG.md b/examples/haskell/CHANGELOG.md index 500a0d0..9e57059 100644 --- a/examples/haskell/CHANGELOG.md +++ b/examples/haskell/CHANGELOG.md @@ -2,4 +2,4 @@ ## 0.1.0.0 -- YYYY-mm-dd -* First version. Released on an unsuspecting world. +- First version. Released on an unsuspecting world. diff --git a/src/config.rs b/src/config.rs index 7e665c4..870f728 100644 --- a/src/config.rs +++ b/src/config.rs @@ -20,6 +20,8 @@ pub struct Root { pub struct FmtConfig { /// Command formatter to run pub command: String, + /// Working directory for formatter + pub work_dir: Option, /// Argument for formatter #[serde(default)] pub options: Vec, diff --git a/src/engine.rs b/src/engine.rs index adf00f4..339ee9a 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -9,8 +9,8 @@ use std::collections::BTreeSet; use std::fs::metadata; use std::iter::Iterator; use std::path::PathBuf; +use std::process::Command; use which::which; -use xshell::cmd; /// Make sure that formatter binary exists. This also for other formatter pub fn check_bin(command: &str) -> Result<()> { @@ -61,6 +61,10 @@ pub fn run_treefmt(cwd: PathBuf, cache_dir: PathBuf) -> anyhow::Result<()> { for c in context { if !c.metadata.is_empty() { println!("Command: {}", c.command); + println!( + "Working Directory: {}", + c.work_dir.clone().unwrap_or("".to_string()) + ); println!("Files:"); for m in &c.metadata { let path = &m.path; @@ -71,17 +75,29 @@ pub fn run_treefmt(cwd: PathBuf, cache_dir: PathBuf) -> anyhow::Result<()> { } // TODO: report errors (both Err(_), and Ok(bad status)) - let _outputs: Vec> = context + let _outputs: Vec> = context .par_iter() .map(|c| { let arg = &c.options; - let cmd_arg = &c.command; + let mut cmd_arg = Command::new(&c.command); + let work_dir = match c.work_dir.clone() { + Some(x) => x, + None => String::new(), + }; let paths = c.metadata.iter().map(|f| &f.path); - cmd!("{cmd_arg} {arg...} {paths...}").output() + if !work_dir.is_empty() { + let _x = std::env::set_current_dir(work_dir); + cmd_arg.args(arg).args(paths).output() + } else { + let _x = std::env::set_current_dir(cwd.clone()); + cmd_arg.args(arg).args(paths).output() + } }) .collect(); - if mfst.manifest.is_empty() || ctxs.is_empty() { + if mfst.manifest.is_empty() && ctxs.is_empty() { + CLOG.info("First time running treefmt"); + CLOG.info("capturing formatted file's state..."); create_manifest(treefmt_toml, cache_dir, old_ctx)?; } else { // Read the current status of files and insert into the manifest. @@ -161,6 +177,7 @@ pub fn create_command_context( Ok(CmdContext { command: config.command.clone(), options: config.options.clone(), + work_dir: config.work_dir.clone(), metadata: path_to_filemeta(list_files)?, }) }) diff --git a/src/eval_cache.rs b/src/eval_cache.rs index d84224d..32b4acd 100644 --- a/src/eval_cache.rs +++ b/src/eval_cache.rs @@ -30,6 +30,7 @@ pub fn create_manifest( let treefmt = cmd.command; let manifest = CmdContext { command: treefmt.to_string(), + work_dir: cmd.work_dir, options: cmd.options, metadata: cmd.metadata, }; @@ -102,15 +103,32 @@ pub fn check_treefmt( cache: &RootManifest, ) -> Result> { let cache_context = cache.manifest.values(); - let results = cmd_context.iter().zip(cache_context); - + let map_ctx: BTreeMap = cmd_context + .into_iter() + .map(|cmd| { + let treefmt = cmd.command.clone(); + let ctx = CmdContext { + command: treefmt.to_string(), + work_dir: cmd.work_dir.clone(), + options: cmd.options.clone(), + metadata: cmd.metadata.clone(), + }; + (treefmt, ctx) + }) + .collect(); + let new_cmd_ctx = map_ctx.values(); + let results = new_cmd_ctx.clone().into_iter().zip(cache_context); let cache_context: Vec = results .clone() .map(|(new, old)| { Ok(CmdContext { command: new.command.clone(), + work_dir: new.work_dir.clone(), options: new.options.clone(), - metadata: if new.command != old.command || new.options != old.options { + metadata: if new.command != old.command + || new.options != old.options + || new.work_dir != old.work_dir + { // If either the command or the options have changed, invalidate old entries new.metadata.clone() } else { diff --git a/src/lib.rs b/src/lib.rs index 475cd20..ef570be 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,6 +21,8 @@ pub static CLOG: CustomLogOutput = CustomLogOutput::new(); pub struct CmdContext { /// formatter command to run pub command: String, + /// formatter work_dir + pub work_dir: Option, /// formatter arguments or flags pub options: Vec, /// formatter target path @@ -30,6 +32,7 @@ pub struct CmdContext { impl PartialEq for CmdContext { fn eq(&self, other: &Self) -> bool { self.command == other.command + && self.work_dir == other.work_dir && self.options == other.options && self.metadata == other.metadata }