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

can cargo fmt or rustfmt format a list of files please? #4485

Closed
gilescope opened this issue Oct 21, 2020 · 11 comments
Closed

can cargo fmt or rustfmt format a list of files please? #4485

gilescope opened this issue Oct 21, 2020 · 11 comments

Comments

@gilescope
Copy link

Pre-commit is widely used and one of the standard approaches is to call a tool with a list of modified files to process. It's pretty common across unix for commands to accept a list of files.

At the moment between cargo fmt and rustfmt there doesn't seem to be a nice way to have pre-commit just run the formatter against just the files that have changed rather than all the files. We can do it by writing some python wrapper around rustfmt but it would be nice if we could just use it out of the box.

@calebcartwright
Copy link
Member

calebcartwright commented Oct 21, 2020

I'm not entirely sure I understand what you are asking for. rustfmt explicitly requires an argument that is either a list of files to format or a string of rust code from stdin. You cannot run rustfmt without providing one or the other. Just make sure to include the --skip-children flag in the rustfmt invocation if you want to skip formatting imported mods from other files.

cargo fmt is the cargo-aware utility used to format an entire workspace (or a specific package when using the -p foo option), which it does by invoking rustfmt with corresponding args and flags. cargo fmt does not accept a list of files because that's not it's intended purpose.

Neither of these is git aware (nor any version control system) by design. There is the git-rustfmt binary (don't recall if this gets distributed with the others via rustup) that attempts to provide some git-awareness driven formatting, and users also have developed their own scripts for detecting which files (and even lines) were modified to pass as the file list to rustfmt (in some cases using the --file-lines option). More info in #1324

@mitsuhiko
Copy link

I think the core issue here is that --skip-children is not actually available on stable rustfmt. The current behavior is very confusing as pointing it to src/main.rs will reformat the entire crate whereas src/other.rs will typically reformat a single file.

@gilescope
Copy link
Author

gilescope commented Nov 16, 2020 via email

@gilescope
Copy link
Author

Oh yeah 5 times faster than black. That's much better. Right will close this - sorry all the talk around rustfmt was about stdin so I didn't realize it did a list of files. It's perfect - many thanks!

@mitsuhiko
Copy link

@gilescope from my experience it doesn't really work. You can pass --config skip_children=false and a single file from what I have seen.

@calebcartwright
Copy link
Member

from my experience it doesn't really work. You can pass --config skip_children=false and a single file from what I have seen.

@mitsuhiko - I agree with you that the current default behavior is not ideal, and one of our planned changes with the as-of-yet-unreleased 2.0 was to invert that model (non-recursive by default with a --recursive flag). However, you can absolutely pass more than one file to rustfmt (this happens all the time when you run cargo fmt).

If you think rustfmt can only format a single file then please open a new issue with steps to reproduce. Discussion of the skip_children behavior/stability is a bit off topic for the question asked here, and we'd want to utilize a new issue for tracking the potential stabilization of skip_children and/or any bugs

@smmoosavi
Copy link

smmoosavi commented Nov 2, 2021

cargo fmt and rustfmt behavior is different. cargo fmt always update all rust files but rustfmt only update mentioned files

cargo fmt -- ./a.rs # it also format b.rs 
rustfmt ./a.rs # only format a.rs

@fraillt
Copy link

fraillt commented Nov 22, 2021

cargo fmt and rustfmt behavior is different. cargo fmt always update all rust files but rustfmt only update mentioned files

cargo fmt -- ./a.rs # it also format b.rs 
rustfmt ./a.rs # only format a.rs

And this is very annoying, we use a stable channel for everything, with the exception to the nightly fmt. So we cannot simply replace cargo fmt with rustfmt as we need rustfmt from a nightly channel.

@calebcartwright
Copy link
Member

I'm a bit puzzled as to why folks are continuing to post increasingly orthogonal comments on an issue that's been closed for over a year.

It also seems there's some general confusion/lack of awareness on the respective tools. The behavior has already been described above but I gather that it's still unclear or folks are simply not reading prior comments, so I'll attempt to explain one more time.

rustfmt is the entirety of the formatter. It accepts the inputs, both target file(s) and configuration options, and does all of the formatting related work. cargo fmt is nothing more than a rustfmt command builder. cargo fmt exists solely to make running rustfmt easier/with fewer key strokes; cargo fmt inspects your local workspace to discover target files and then literally just calls rustfmt.cargo fmt is not some separate tool, and it cannot be used/provides no utility when rustfmt is not present.

If you run cargo fmt with the --verbose option, it will show you the rustfmt command(s) it invokes:

$ cargo new foo && cd foo
$ cargo fmt

Will result in rustfmt src/main.rs --edition 2018 being executed (note the edition value may be 2021 instead, depending on how the new foo project was created based on your local default toolchain version). That cargo fmt derived invocation is exactly the same as running the rustfmt command yourself.

rustfmt accepts/requires a list of input files, cargo fmt discovers and specifies the input files on your behalf so that you don't have to. This is why cargo fmt does not, and will not, accept a list of input files. We're exploring an option that would be useful for IDE/editor plugins, but there's really no need and no benefit for a human to call cargo fmt to format a single file/subset of files (because determining the target files is the main thing cargo fmt does). If you want to manually specify a specific set of files then you should call rustfmt yourself, the channel/stability commentary is irrelevant on the usage of cargo fmt vs rustfmt.

Yes, it's true that rustfmt has historically operated in a "recursive by default" mode where rustfmt formats the provided input files and the associated files for any out-of-line modules that are imported. However, that's quickly getting off topic from the original question of this issue. Additionally, that current default behavior is something that's already been discussed at length in various other issues and we've already conveyed our intent to reverse that behavior (non-recursive by default with a recursive opt-in flag). There's not really any benefit in rehashing those prior discussions again on an issue that was closed by the author.

Again, this is rustfmt behavior that makes no difference whether a human directly invokes rustfmt or whether cargo fmt is what invoked rustfmt. This recursive by default behavior applies everywhere which is why this prior comment:

rustfmt ./a.rs # only format a.rs

is false. If a happens to import any out-of-line modules those will get formatted too.

@Wilfred
Copy link

Wilfred commented May 12, 2022

I bumped into this recently, and I found two ways to format a single file:

$ cat file.rs | rustfmt --emit=stdout --quiet

$ rustfmt --emit=stdout --quiet --unstable-features --skip-children file.rs

@untitaker
Copy link

but there's really no need and no benefit for a human to call cargo fmt to format a single file/subset of files (because determining the target files is the main thing cargo fmt does)

The other thing that cargo fmt does is to pass the correct edition defined in Cargo.toml to rustfmt. The current situation is that the edition has to be defined separately in the precommit hook for its own invocation of rustfmt, and kept in sync with Cargo.toml

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

7 participants