This is annoying because a lot of times a command will "fail" even though "failure" isn't an exceptional condition.
would-reformat - automatic reformatting for emacs without being annoying
Motivation
I've never liked it when a text editor formats code for me without my asking. I don't like it when the editor uses cheap heuristics (indent a new line to match the previous one? blah!) and I don't like it when the editor shells out to one of the new generation of automatic code formatters.
But I don't enjoy formatting code by hand either. Who does?
This is my solution: some shell scripts and some emacs lisp code.
The shell script would-reformat.sh takes a file's full path as
its argument. It tells you if that file would be reformatted.
The shell script do-reformat.sh takes a file's full path as its argument. It
reformats the file.
Finally, there is some rudimentary machinery to run would-reformat.sh when
saving a file and to run do-reformat.sh when you hit <F1>.
Supported programming languages and file formats
- css
- dart
- go
- html
- javascript
- jsx
- php
- python
- scss
- sh
- tsx
- typescript
- vue
Python uses black; go uses gofmt; dart uses dart format. The rest use
prettier. Adding new programming languages is easy, assuming they have a
formatter with a dry run mode.
Installation
- Change to the directory where you want to use would-reformat:
you@host:~ $ cd ~/devel/my-project - From that directory, call the script
install.shin the directory where you have would-reformat checked out:you@host:~/devel/my-project $ ~/src/would-reformat/install.sh
Be sure to run the installation script from the root of your project's directory, not a subdirectory.
This will do the following:
- create the directory
~/devel/my-project/binif it does not exist - create
~/devel/my-project/bin/_reformat-common.sh,~/devel/my-project/bin/do-reformat.sh, and~/devel/my-project/bin/would-reformat.shas symlinks to files with the same names in~/src/would-reformat. - create
~/devel/my-project/.dir-locals.elas a symlink to the file with the same name in~/src/would-reformat.
This should work from a checkout wherever you happen to have it. It doesn't
need to be in ~/src. However, if you remove the checkout, then the symlinks
will break, so don't do that. If you rename the checkout, the symlinks will break.
The directory bin with respect to my-project however is hardcoded
(enhancements here are welcome).
The script install.sh favors conservatism over cleverness: for example,
if it finds the file ~/devel/my-project/bin/do-reformat.sh then it
checks whether it is a symlink to ~/src/would-reformat/do-reformat.sh.
If it is not, then it just prints a warning rather than attempting to correct
the situation.
Troubleshooting
nothing works from emacs
Be sure to run the install script from your project's root as determined by
(projectile-project-root).
problems with nvm, pyenv, goenv, etc
The problem might be that their shims aren't in your PATH. To work around
this sort of problem I run emacs from a wrapper script that ensures the shims
are loaded in my PATH.
Running prettier is slow
The scripts by default call npx prettier with some arguments. If you call
these scripts from a directory where there's a node_modules/ directory with
prettier and plugins, if necessary, installed, then npx should be fast
enough.
The solution, therefore, seems to be to say yarn add --dev prettier (or
npm install --dev prettier) in the right place.
(This seems to work for me, but it is admittedly still uncomfortably cargoculty. There should be a more solid basis for this answer.)
problems running black
The solution seems to be to run pip install black pipx somewhere.
Development
Wishlist
- Generalize this beyond emacs
- In emacs, provide a minor mode or something to use instead of
.dir-locals.el, which I've always found to be very fragile - Add support for more programming languages and file formats
- Clean up shell script code
- Be better at "just working"
- Make it easy to add "private" and alternate formatters
Problems
- It would probably be better if we wrote reformatted code to temp files and atomically renamed them
- This probably doesn't work on Windows as-is.
- It doesn't work with tramp.