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 ``. ## Supported programming languages and file formats - css - dart - go - html - javascript - jsx - php - python - scss - sh - tsx - typescript - vue Out of the box, python uses `isort` and `black`, go uses `gofmt`, and 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.sh` in 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/bin` if 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.sh` as symlinks to files with the same names in `~/src/would-reformat`. - create `~/devel/my-project/.dir-locals.el` as 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. FIXME: Document how to reinstall if the checkout was moved, incompatible changes were made, etc. # Customization For information on changing how `would-format` handles different files, see [CUSTOMIZATION.md](CUSTOMIZATION.md). # Development If you want to hack on `would-format`, see [DEVELOPMENT.md](DEVELOPMENT.md) for more info. # 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. ## I need to use one version of `would-reformat` with one of my projects and another with another With the per-project configuration [see below] this should rarely be necessary, but in case it is, one thing you can try is creating different `would-reformat` checkouts for different projects. For example, something like this might work: ``` $ ls ~/devel new-project1 new-project2 old-and-weird-project1 $ git clone https://github.com/maw/would-reformat $ cd new-project1; ../would-reformat/install.sh $ cd ../new-project2; ../would-reformat/install.sh $ cd .. $ git clone https://github.com/maw/would-reformat would-reformat-for-old-and-weird-project1 $ cd would-reformat-for-old-and-weird-project1; git checkout vSome-old-tag $ cd ../old-and-weird-project1; ../would-reformat-for-old-and-weird-project1/install.sh ``` # Similar projects If you don't like `would-reformat`'s approach, maybe one or more of these would be more suitable. - https://github.com/purcell/emacs-shfmt/ - https://github.com/pythonic-emacs/blacken - https://github.com/dominikh/go-mode.el