Compare commits

70 Commits

Author SHA1 Message Date
Michael Wolf
c4cac19501 Document situation to be signaled
Lame formatters cannot distinguish between when there are syntax errors and
when code should be reformatted.
2024-10-30 21:59:40 -06:00
Michael Wolf
44fd5a350f Improve documentation slightly 2024-10-30 21:59:10 -06:00
Michael Wolf
e8771f1156 Add shfmt.sh 2024-04-03 11:29:32 -06:00
Michael Wolf
7c503cecf7 Try to do a few things
FIXME: do them correctly; squash and reword this travesty of a commit.
2024-04-03 11:26:50 -06:00
Michael Wolf
1957698fc4 Update years in license 2024-04-03 11:26:36 -06:00
Michael Wolf
970a92def9 Update some docs 2024-04-03 11:26:30 -06:00
Michael Wolf
d16dfaff1f Add doc re new formatters 2024-04-03 11:26:15 -06:00
Michael Wolf
b54ee4a69e Note an obvious error 2024-03-13 09:49:38 -06:00
Michael Wolf
897a91b350 Bake in support for rustfmt 2023-12-07 09:53:21 -06:00
Michael Wolf
5c987ae777 Fix gofmt.sh 2023-09-30 20:59:19 -06:00
Michael Wolf
43688e8615 Add a todo 2023-09-30 20:44:06 -06:00
Michael Wolf
eeee488dde Add/update a lot of documentation 2023-09-30 20:38:00 -06:00
Michael Wolf
87e15f87f3 Teach do-reformat.sh to use the more extensible stuff 2023-09-30 19:05:52 -06:00
Michael Wolf
67027cd612 Be more consistent 2023-09-30 19:05:26 -06:00
Michael Wolf
cb330256d4 Use custom wrapper when it's available 2023-09-30 19:04:39 -06:00
Michael Wolf
2aa478eea6 Move and correct defn
The salient difference is that it uses -x when testing.
2023-09-30 19:04:02 -06:00
Michael Wolf
973b9e4c2f Move wrapper selection into a function 2023-09-30 18:14:47 -06:00
Michael Wolf
9a752a45ba Make shellcheck happier 2023-09-30 18:13:46 -06:00
Michael Wolf
f31cc2d4a2 Create and use dart_format.sh wrapper script 2023-09-30 18:05:36 -06:00
Michael Wolf
50cbaa0454 Remove debug spew and undefined variable 2023-09-30 17:49:00 -06:00
Michael Wolf
7866b6de55 Create and use gofmt.sh wrapper script 2023-09-30 17:48:46 -06:00
Michael Wolf
0273d5762e Fix up prettier wrapper script 2023-09-30 17:33:20 -06:00
Michael Wolf
4c598c589e Instruct shellcheck to follow sourced files 2023-09-30 17:32:55 -06:00
Michael Wolf
358d2a4b14 Make shellcheck happier 2023-09-30 17:32:48 -06:00
Michael Wolf
5d3dc8003e Make shfmt happier 2023-09-30 17:30:15 -06:00
Michael Wolf
9936c641a2 Make shfmt happier 2023-09-30 17:30:07 -06:00
Michael Wolf
ec7c495963 Make shfmt happier 2023-09-30 17:29:34 -06:00
Michael Wolf
1114130e5e Work around non-failure "failure" modes 2023-09-30 17:29:22 -06:00
Michael Wolf
83f4e1c3e6 Make shfmt happier 2023-09-30 17:29:05 -06:00
Michael Wolf
60f934680d Nix some debug output 2023-09-30 17:28:50 -06:00
Michael Wolf
bb9b380382 Get and use PROJECT_ROOT and WF_ROOT early 2023-09-30 17:28:23 -06:00
Michael Wolf
e587f8aebe Install _wrflog.sh 2023-09-30 17:27:53 -06:00
Michael Wolf
555a9b3d7e Make shfmt happier 2023-09-30 17:27:49 -06:00
Michael Wolf
a1aaa08747 Make shfmt happier 2023-09-30 17:27:38 -06:00
Michael Wolf
4b56817b2d Revert "Don't call get_os needlessly"
This reverts commit 08be19fe28.
2023-09-30 17:25:43 -06:00
Michael Wolf
3793a11fbf Use set -e
This is annoying because a lot of times a command will "fail" even though
"failure" isn't an exceptional condition.
2023-09-30 17:21:23 -06:00
Michael Wolf
3d8ba44afd Add some early logging possiblity 2023-09-30 17:20:59 -06:00
Michael Wolf
0414802fa8 Unset variables before running each test 2023-09-30 17:19:54 -06:00
Michael Wolf
4d05d3da71 Add addl wf_root test 2023-09-30 17:19:47 -06:00
Michael Wolf
c2fe100ad9 Nix needless shift stmt 2023-09-30 17:18:47 -06:00
Michael Wolf
5319677a4c Fix wf_root 2023-09-30 17:18:26 -06:00
Michael Wolf
21736cf0a2 Recycle wf_root output 2023-09-30 17:17:58 -06:00
Michael Wolf
fd384a91ea Recycle project_root output 2023-09-30 17:17:00 -06:00
Michael Wolf
08be19fe28 Don't call get_os needlessly 2023-09-30 17:16:50 -06:00
Michael Wolf
ce634e496c Add initial support for custom sniffing 2023-09-30 15:33:25 -06:00
Michael Wolf
3034cd23e8 Fix 2023-09-30 15:32:00 -06:00
Michael Wolf
032f32f132 Make shellcheck happier 2023-09-30 15:30:23 -06:00
Michael Wolf
a5ca80af47 Add scripts to run shellcheck and shfmt 2023-09-30 15:29:37 -06:00
Michael Wolf
7ae2eed832 Make shellcheck happier 2023-09-30 14:57:13 -06:00
Michael Wolf
d6755810e4 Add file for in-process tests
We can just run this file without disabling the other tests manually.
2023-09-30 14:56:03 -06:00
Michael Wolf
7322fc5f71 Make shfmt happier 2023-09-30 14:55:00 -06:00
Michael Wolf
952c7eeb90 Add various utility functions 2023-09-30 14:54:11 -06:00
Michael Wolf
d4f5fbdaf8 Add addl test 2023-09-30 14:53:47 -06:00
Michael Wolf
7ed9ffce4a Add addl tests 2023-09-30 14:23:34 -06:00
Michael Wolf
c59808ecc1 Add first cut at some tests 2023-09-30 12:20:58 -06:00
Michael Wolf
2926dba991 Make shellcheck happier 2023-09-20 10:18:42 -06:00
Michael Wolf
9966d65489 Make shellcheck happier 2023-09-20 10:15:48 -06:00
Michael Wolf
78bcb0dd2a Make shellcheck happier 2023-09-20 10:11:01 -06:00
Michael Wolf
0c667db03c Make shellcheck happier 2023-09-20 10:10:50 -06:00
Michael Wolf
5f9d2f7679 Make shellcheck happier 2023-09-20 10:10:04 -06:00
Michael Wolf
d486fe6217 Make shellcheck happier 2023-09-20 10:09:35 -06:00
Michael Wolf
607140cce3 Use use prettier wrapper script to reformat 2023-09-20 07:38:42 -06:00
Michael Wolf
4e5f5b1855 Don't redundantly set variable 2023-09-20 07:16:38 -06:00
Michael Wolf
b4f69b21cf Extract isort and black 2023-09-20 07:15:33 -06:00
Michael Wolf
1e942180fe List some problems 2023-09-20 07:09:52 -06:00
Michael Wolf
df2658dcd5 Remove redundant execution 2023-09-20 07:09:45 -06:00
Michael Wolf
8d6ee796b9 Adapt to renamed file 2023-09-18 17:20:18 -06:00
Michael Wolf
b0ea0360af Simplify function 2023-09-18 17:19:10 -06:00
Michael Wolf
0f2a06e1f0 Add FIXME 2023-09-18 17:19:05 -06:00
Michael Wolf
2cc53eb7e1 Start extracting language-specific formatters 2023-09-18 16:44:26 -06:00
26 changed files with 1173 additions and 135 deletions

3
.gitignore vendored
View File

@@ -1,3 +1,4 @@
.dir-locals.el
bin/
node_modules/
node_modules/
.root

209
CUSTOMIZATION.md Normal file
View File

@@ -0,0 +1,209 @@
There are two main areas of per-project customization: how `would-format`
determines a given file's type, and how a file is checked or reformatted
after its type has been determined.
# Conventions
`$YOUR_PROJECT_ROOT` is the root where your project is checked out.
# File type sniffing
To change a file's mapping, create a script at
`$YOUR_PROJECT_ROOT/.would-reformat/custom-sniffer`. It should take one
argument, the fully qualified path of the file whose type is to be sniffed.
It should emit the type of file it thinks its argument is.
Here's an example:
```shell-script
#!/usr/bin/env bash
file="$1"
if [[ $file == *.pl ]] ; then
# the default is that .pl corresponds to perl, but not here!
echo -n "prolog"
fi
if [[ $file == *.ts ]] ; then
# qt translations, who knew
echo -n "qt-translation"
fi
# We're fine with the defaults for other types of files so we don't
# print anything else.
exit 0
```
(This file, possibly with updates, is also available
[here](./examples/custom-sniffer).)
Now, if you run this script against a file that ends in `.pl` or `.ts`,
`would-reformat` will not treat the file as perl or typescript, which is what
happens by default, but instead as prolog or a qt-translation file. But if
you run this script against a file that ends in `.py` then it'll continue to
be treated as python as usual.
You can perform arbitrarily sophisticated checks here. They're not limited to
file names or extensions. You can take paths into account. You can can even
take files' contents into account. But do keep in mind that this program
will be run often. If it's slow you'll be frustrated by it.
This program can be written in something other than shell. This might make
implementation easier, and it might be worthwhile if performance is a serious
concern. It doesn't actually need to be a "script".
Finally, keep in mind that if `would-reformat`'s defaults work for you, there
is no need to have this file at all.
## Acceptable output
<!-- FIXME: I can't remember the syntax to include and escape ., _, and -.
But those would be OK too, although starting with . or - is usually
more trouble than it's worth. -->
In general, you would do well to limit it to `/[0-9a-z]+/`. It must not
include the `/` character. `would-reformat` doesn't care if you use
uppercase, but some case-preserving filesystems make things difficult.
It must not output `custom-sniffer`. If there is a hitherto unknown
programming language named "custom sniffer" you'll have to output something
else.
## Return values
`would-reformat` expects `custom-sniffer` to return one of the following
values:
- 0: success
- 254: file can't be read due to permissions, etc ("etc" is as of
2023-Q3 broad in scope; it could include trying and failing to read a file
on a NFS mount)
- 255: some sort of internal error
# Custom reformatter wrappers
Your custom wrappers live in `$YOUR_PROJECT_ROOT/.would-reformat`. Its name
should be the file type as identified by the sniffer.
It takes one command line argument, the fully qualified path of the file to be
checked or reformatted. It also receives input via three environment
variables:
- `WOULD_REFORMAT` can be set to either `would_reformat` or `do_reformat`. If
the former, it should run in non-destructive mode. If the latter, it should
perform changes on file whose path was passed as the main argument.
- `PROJECT_ROOT` has the value of `$YOUR_PROJECT_ROOT`.
- `WF_ROOT` holds the path where `would-format` is checked out.
Often, these scripts won't need to check `$PROJECT_ROOT` or `$WF_ROOT`.
`$WOULD_REFORMAT` is indepensable.
Here is an example:
```shell-script
#!/usr/bin/env bash
set -euo pipefail
IFS=$'\n\t'
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
## This usually isn't necessary, but if you need to use wrflog this
## is how you get it:
# source "$WF_ROOT/_reformat-common.bash"
if [[ "$WOULD_REFORMAT" = "would_reformat" ]] ; then
set +e
out=$(php $DIR/.././vendor/bin/pint --test "$1")
retval="$?"
set -e
# unfortunately, pint doesn't distinguish between files with
# syntax errors and files that are merely misformatted
echo "$out"
exit "$retval"
fi
if [[ "$WOULD_REFORMAT" = "do_reformat" ]] ; then
set +e
out=$(php $DIR/.././vendor/bin/pint "$1")
retval="$?"
set -e
echo "$out"
exit "$retval"
fi
exit 255
```
(This file, possibly with updates, is also available
[here](./examples/php).)
As with `custom-sniffer`, a reformatter doesn't have to be written as a shell
script. It just needs to be properly named and executable.
## Acceptable output
As output, this script should emit whatever the programs it calls emit.
## Return values
### In `would_format` mode
`would-format.sh` interprets return values to mean the following:
- 0: file wouldn't be reformatted
- 1: file would be reformatted
- 2: file has at least one syntax error
- 3: file would be reformatted or it has at least one syntax error but we
cannot distinguish which
- 252: Unexpected return value from the tool; that is, the value of `$?` is
unexpected and thus unhandled
- 253: Unexpected output from the tool; that is, the output emitted by the
tool is unexpected and thus unhandled
- 254: file can't be read due to permissions, etc ("etc" is as of
2023-Q3 broad in scope; it could include trying and failing to read a file
on an NFS mount)
- 255: error internal to the script in question
(FIXME: Unsurprisingly, `gofmt` is the only tool I've tested so far that gets
this right. It isn't an exceptional condition when a file should be
reformatted, so there's no good reason to signal failure here.
Even less unsurprisingly, Laravel pint gets it worse than all the others I
checked; it doesn't distinguish files that need to be reformatted from files
with actual syntax errors.
If possible, then, we should return 1 or 2 when relevant. When they can't be
distinguished, return 3.)
### in `do_format` mode
`do-reformat` expects a wrapper to return one of the following
values:
- 0: file was successfully reformatted
- 1: file was not successfully reformatted, presumably due to a syntax error
- 254: file can't be rewritten due to permissions, etc ("etc" is as of
2023-Q3 broad in scope; it could include trying and failing to read or write
a file on an NFS mount)
- 255: error internal to the script in question
# What goes into version control and what does not?
FIXME: Write this section
# What files can I modify?
You should not modify any of the files in `.would-reformat/bin` or the files
that they link to. Everything else in `.would-reformat` is fair game.
Of course, `would-reformat` is FOSS. Within the limitations set by its
license, you can do what you like with it. But if you want to make deeper
changes, you're probably better off forking and going from there.

79
DEVELOPMENT.md Normal file
View File

@@ -0,0 +1,79 @@
# Dependencies
In addition to a fairly recent version of bash, it's helpful to have the
following tools installed:
- [shunit2](https://github.com/kward/shunit2)
- [shellcheck](https://www.shellcheck.net/)
- [shfmt](https://github.com/mvdan/sh)
In general, code should pass shellcheck without warnings, be formatted with
shfmt (please use it with `-i 4`), and when relevant come with new tests and
pass them.
If any of this causes you trouble, don't worry. Please submit your pull
request anyway and we can work on getting it up to snuff.
# General goals and non-goals
## Goals
- Be as batteries-included as possible. Go out of our way to just work, even
if not entirely optimally, in as many situations as possible.
- Support "relatively isolated" projects.
- Be easy to configure.
- Be clearly documented. Remember that many programmers are working in
high-stress situations. Documentation that is incomplete, unclear, or cute
makes things even worse for them. Use English correctly and, to the extent
possible, unambiguously.
## Non-goals
- We don't want to do anything with warnings etc that aren't specifically
formatting-related
# 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
- It should signal more clearly when the file has a syntax error, subject to
the limitations set by third party tools.
- Would global (per-user) customization be useful? My guess is that it'd
probably be more confusing than helpful but I'm not wedded to this
conclusion.
- Write a doctor command to identify potential issues (you'd probably run it
similarly to how you run the install script, but `s/install/doctor/`).
- Create a nix setup that includes all the tools as fallbacks
- Cache file type sniffing results? How?
# Problems
- It would probably be better if we wrote reformatted code to temp files and
atomically renamed them. This might mean that the tool-specific programs
should emit diffs rather than working silently as they currently do. The
rationale is that this makes it a lot easier to atomically apply two or more
fixers.
- It probably doesn't work on Windows as-is.
- There are probably some Linuxisms and maybe even some recentbashisms.
- It doesn't work with tramp.
- As long as it's written in shell, it's always going to be a little bit slow
and, of course, fairly difficult to hack on. If the bulk of it were
rewritten in, say, go, then some problems would go away and some new ones
would be created. On balance I'm not sure if it's a good idea or not, but
I'm tempted.
- Stop writing files to `$PROJECT_ROOT/bin`; use
`$PROJECT_ROOT/.would-reformat/bin` instead.
- `PROJECT_ROOT` and `WF_ROOT` are probably too generic. Consider prefixing
them, maybe with `WOULD_FORMAT_`.
# To do
- Some errors internal to `would-reformat` aren't properly signalled. See
[RETURN-VALUES.md](RETURN-VALUES.md) for some. Others still need to be
defined.

View File

@@ -1,6 +1,6 @@
MIT License
Copyright (c) 2020, 2021, 2022, 2023 Michael Wolf
Copyright (c) 2020, 2021, 2022, 2023, 2024 Michael Wolf
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

91
NEW-FORMATTERS.md Normal file
View File

@@ -0,0 +1,91 @@
## available environment variables
- `PROJECT_ROOT` is the root directory of your project, the one
with files that need to be reformatted. Currently (as of 2023-09) this is
just the output of `git rev-parse --show-toplevel` but it may be improved.
- `WF_ROOT` is the root directory of the `would-format` checkout
file checking and sniffing
## file type sniffing
You can override a file type by creating an executable in
`$WOULD_FORMAT_PROJECT_ROOT/.would-format/sniffer`. This is typically a shell
script, but it doesn't have to be. It takes one argument, the full path of
the file to be checked.
This file should print the type of file it should be treated as. If the
script has no opinion on the matter, it should print nothing and exit. Here
is an example:
```shell-script
#!/usr/bin/env bash
file="$1"
if [[ $file == *.pl ]] ; then
# the default is that .pl corresponds to perl, but not here!
echo -n "prolog"
fi
if [[ $file == *.ts ]] ; then
# qt translations, who knew
echo -n "qt-translation"
fi
# We're fine with the defaults for other types of files so we don't
# print anything else.
exit 0
```
Return values:
- 0: success
- 254: file can't be read due to permissions, etc ("etc" is as of
2023-Q3 broad in scope; it could include trying and failing to read a file
on a NFS mount)
- 255: some sort of internal error
## return value normalization for formatters
In general, low-numbered codes are related to the code to be analyzed
or reformatted and high-numbered codes have to do with problems with these
scripts themselves.
### In `would_format` mode
`would-format.sh` interprets return values to mean the following:
FIXME: Unsurprisingly, `gofmt` gets this right, even if none of the other
tools does. Drop `1`.
- 0: file wouldn't be reformatted
- 1: file would be reformatted
- 2: file has at least one syntax error
- 3: file would be reformatted or it has at least one syntax error but we
cannot distinguish which
- 252: Unexpected return value from the tool; that is, the value of `$?` is
unexpected and thus unhandled
- 253: Unexpected output from the tool; that is, the output emitted by the
tool is unexpected and thus unhandled
- 254: file can't be read due to permissions, etc ("etc" is as of
2023-Q3 broad in scope; it could include trying and failing to read a file
on a NFS mount)
- 255: error internal to the script in question
# in `do_format` mode
`do-format.sh` interprets return values to mean the following:
- 0: file was successfully reformatted
- 1: file was not successfully reformatted, presumably due to a syntax error
- 254: file can't be rewritten due to permissions, etc ("etc" is as of
2023-Q3 broad in scope; it could include trying and failing to read a file
on a NFS mount)
- 255: error internal to the script in question

View File

@@ -1,4 +1,4 @@
would-reformat - automatic reformatting for emacs without being annoying
would-reformat - automatic reformatting for emacs (and maybe other editors) without being annoying
# Motivation
@@ -38,16 +38,20 @@ saving a file and to run `do-reformat.sh` when you hit `<F1>`.
- 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.
Out of the box, for python we use `isort` and `black`, for go we use `gofmt`,
for dart we use `dart format`, for rust we use `rustfmt`, and for everything
else we use `prettier`.
Adding suuport for other programming languages is easy, assuming they have a
formatter with a dry run mode. And even if they don't it shouldn't be that
bad.
# Installation
- Change to the directory where you want to use **would-reformat**:
- 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 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
@@ -65,7 +69,8 @@ This will do the following:
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.
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).
@@ -76,6 +81,20 @@ 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
@@ -106,14 +125,31 @@ cargoculty. There should be a more solid basis for this answer.)
The solution seems to be to run `pip install black pipx` somewhere.
# Development
## I need to use one version of `would-reformat` with one of my projects and another with another
# Wishlist
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:
- 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
```
$ 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

5
RETURN-VALUES.md Normal file
View File

@@ -0,0 +1,5 @@
To avoid confusion, we should probably stay away from numbers that are low and
number that are near 255 as much as we can.
101 - problem with pushd
102 - problem with popd

View File

@@ -1,10 +1,128 @@
function root() {
echo "$(git rev-parse --show-toplevel)"
function log () {
msg=$@
if [[ ! -z "${LOG_TO+}" ]] ; then
echo "$msg" >> $LOG_TO
fi
}
function get_os() {
un=$(uname)
if [[ "$un" = "Linux" ]]; then
echo -n "linux"
return 0
fi
if [[ "$un" = "Darwin" ]]; then
echo -n "osx"
return 0
fi
}
OPERATING_SYSTEM=$(get_os)
# For a given file, return its corresponding project root.
function project_root() {
set +u
if [[ ! -z "${PROJECT_ROOT}" ]]; then
echo "$PROJECT_ROOT"
return 0
fi
set -u
pushd "$(dirname "$1")" >/dev/null 2>&1 || exit 101
out=$(git rev-parse --show-toplevel)
ret="$?"
popd >/dev/null 2>&1 || exit 1
if [[ "$ret" != "0" ]]; then
echo -n ""
return 1
fi
echo -n "$out"
return 0
}
# When run from a dir with would-format configured, return
# would-format's own directory
wf_root() {
set +u
if [[ ! -z "${WF_ROOT}" ]]; then
echo "$WF_ROOT"
return 0
fi
set -u
arg=$(pwd)/bin/would-reformat.sh
PROJECT_ROOT=$(project_root "$arg")
pushd "$PROJECT_ROOT" 2>&1 >/dev/null
pr="$PROJECT_ROOT"
link="$pr/bin/would-reformat.sh"
original0=$(resolve_symlink "$link")
original=$(normalize_dir_of_file "$original0")
out="$original"
popd 2>&1 >/dev/null
echo "$out"
return 0
}
# FIXME: Make this bail if it fails
function normalize_dir_of_file() {
out0=$(readlink -m "$1")
out=$(dirname "$out0")
echo -n "$out"
return 0
}
function resolve_symlink() {
if [[ "$OPERATING_SYSTEM" = "osx" ]]; then
out=$(readlink "$1")
else
out=$(readlink -f "$1")
fi
echo "$out"
}
function custom_sniff() {
file="$1"
pr=$(project_root "$file")
if [[ -f "$pr/.would-reformat/custom-sniffer" ]]; then
custom=$("$pr/.would-reformat/custom-sniffer" "$1")
ret="$?"
echo "$custom"
return 0
fi
}
## WOULD_FORMAT_PROJECT_ROOT=$(project_root $1)
# FIXME: This needs to be made customizable
function sniff_file_type() {
ff=$1
shift
maybe_custom=$(custom_sniff "$ff")
if [[ "$maybe_custom" != "" ]]; then
echo "$maybe_custom"
return 0
fi
file_type=
if [[ $ff == *.py ]]; then
@@ -25,17 +143,81 @@ function sniff_file_type() {
file_type="jsx"
elif [[ $ff == *.tsx ]]; then
file_type="tsx"
elif [[ $ff == *.css ]] ; then
elif [[ $ff == *.css ]]; then
file_type="css"
elif [[ $ff == *.scss ]] ; then
elif [[ $ff == *.scss ]]; then
file_type="scss"
elif [[ $ff = *.sh ]]; then
file_type="sh"
elif [[ $ff = *.go ]] ; then
elif [[ $ff = *.go ]]; then
file_type="golang"
elif [[ $ff = *.dart ]] ; then
elif [[ $ff = *.dart ]]; then
file_type="dart"
elif [[ $ff = *.sh ]]; then
# .sh isn't enough; we'll need other extns
file_type="shell"
elif [[ $ff = *.pl ]]; then
file_type="perl"
elif [[ $ff = *.rs ]] ; then
file_type="rust"
fi
echo $file_type
}
function custom_wrapper() {
root="$1"
file_type="$2"
maybe="$root/.would-reformat/$file_type"
if [[ -x "$maybe" ]]; then
echo -n "$maybe"
return
fi
echo ""
}
function choose_wrapper() {
file_type="$1"
custom=$(custom_wrapper $PROJECT_ROOT $file_type)
if [[ ! -z "${custom}" ]]; then
echo -n "$custom"
return
fi
if [[ $file_type = "javascript" ||
$file_type = "vue" ||
$file_type = "typescript" ||
$file_type = "css" ||
$file_type = "php" ||
$file_type = "html" ||
$file_type = "jsx" ||
$file_type = "tsx" ||
$file_type == "css" ||
$file_type == "scss" ]]; then
out="$WF_ROOT"/./prettier.sh
elif [[ $file_type == "python" ]]; then
out="$WF_ROOT"/./isort-and-black.sh
elif [[ $file_type == "golang" ]]; then
out="$WF_ROOT"/./gofmt.sh
elif [[ $file_type == "rust" ]] ; then
out="$WF_ROOT"/./rustfmt.sh
elif [[ $file_type == "dart" ]]; then
out="$WF_ROOT"/./dart_format.sh
elif [[ $file_type == "shell" ]] ; then
out="$WF_ROOT"/./shfmt.sh
else
out="$WF_ROOT"/./default.sh
fi
echo "$out"
}
function wrflog() {
echo "$@" >>/tmp/wrflog
}
# WOULD_FORMAT_INSTALLATION_ROOT=$(wfroot)
export WOULD_FORMAT_PROJECT_ROOT

View File

@@ -1 +0,0 @@
_reformat-common.bash

5
_wrflog.sh Normal file
View File

@@ -0,0 +1,5 @@
# shellcheck shell=bash
function wrflog() {
echo "$@" >>/tmp/wrflog
}

7
ci/shellcheck.sh Executable file
View File

@@ -0,0 +1,7 @@
#!/bin/bash
files=$(find . -type f | grep -v '\.git' | grep '\.sh')
for i in $files; do
shellcheck -x "$i"
done

3
ci/shfmt.sh Executable file
View File

@@ -0,0 +1,3 @@
#!/bin/bash
docker run --rm -u "$(id -u):$(id -g)" -v "$PWD:/mnt" -w /mnt mvdan/shfmt -l -i 4 .

41
dart_format.sh Executable file
View File

@@ -0,0 +1,41 @@
#!/bin/bash
set -uo pipefail
IFS=$'\n\t'
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$DIR/_reformat-common.bash"
set -e
file="$1"
pushd "$(dirname "$file")" 2>&1
if [[ "$WOULD_REFORMAT" = "would_reformat" ]]; then
# probably others too :(
syntax_error_retval=65
set +e
out=$(dart format -o none --set-exit-if-changed "$file")
retval="$?"
set -e
if [[ "$retval" = "$syntax_error_retval" ]]; then
retval=2
fi
exit "$retval"
fi
if [[ "$WOULD_REFORMAT" = "do_reformat" ]]; then
# FIXME this is obviously obviously obviously wrong
out=$(npx prettier --write "$file" 2>&1 >/dev/null)
retval="$?"
echo "$out"
exit "$retval"
fi
exit 255

4
default.sh Normal file
View File

@@ -0,0 +1,4 @@
#!/bin/bash
echo -n "unhandled file type; ignoring"
exit 0

View File

@@ -5,44 +5,33 @@ IFS=$'\n\t'
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$DIR/_reformat-common.sh"
source "$DIR/_reformat-common.bash"
#export LOG_DEST="$LOG_TO"
file="$1"
file_type=$(sniff_file_type $file)
PROJECT_ROOT=$(project_root "$file")
export PROJECT_ROOT="$PROJECT_ROOT"
WF_ROOT=$(wf_root)
export WF_ROOT="$WF_ROOT"
if [[ $file_type == "javascript" || \
$file_type == "vue" || \
$file_type == "css" || \
$file_type == "typescript" || \
$file_type == "php" || \
$file_type == "html" || \
$file_type == "jsx" || \
$file_type == "tsx" || \
$file_type == "css" || \
$file_type == "scss" || \
$file_type == "sh" ]]; then
out=$(npx prettier --write $file)
retval="$?"
success_retval=0
elif [[ $file_type == "python" ]]; then
out=$(pipx run black $file 2>/dev/null && pipx run isort --profile black $file 2>/dev/null)
retval="$?"
success_retval=0
elif [[ $file_type == "golang" ]] ; then
out=$(gofmt -w "$file" 2>/dev/null)
retval="$?"
success_retval=0
elif [[ $file_type == "dart" ]] ; then
out=$(dart format $file)
retval="$?"
success_retval=0
else
echo -n "ignoring"
exit 0
fi
file_type=$(sniff_file_type "$file")
if [[ x"$retval" == x"$success_retval" ]]; then
export WOULD_REFORMAT=do_reformat
wrapper=$(choose_wrapper "$file_type")
wrflog got wrapper: $wrapper
set +e
out=$("$wrapper" "$file" 2>&1 >/dev/null)
retval="$?"
set -e
success_retval=0
if [[ "$retval" == "$success_retval" ]]; then
echo "$out"
exit 0
else

18
examples/custom-sniffer Executable file
View File

@@ -0,0 +1,18 @@
#!/usr/bin/env bash
file="$1"
if [[ $file == *.pl ]]; then
# the default is that .pl corresponds to perl, but not here!
echo -n "prolog"
fi
if [[ $file == *.ts ]]; then
# qt translations, who knew
echo -n "qt-translation"
fi
# We're fine with the defaults for other types of files so we don't
# print anything else.
echo ""
exit 0

36
examples/php Normal file
View File

@@ -0,0 +1,36 @@
#!/usr/bin/env bash
set -euo pipefail
IFS=$'\n\t'
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
## This usually isn't necessary, but if you need to use wrflog this
## is how you get it:
# source "$WF_ROOT/_reformat-common.bash"
if [[ "$WOULD_REFORMAT" = "would_reformat" ]] ; then
set +e
out=$(php $DIR/.././vendor/bin/pint --test "$1")
retval="$?"
set -e
# unfortunately, pint doesn't distinguish between files with
# syntax errors and files that are merely misformatted
echo "$out"
exit "$retval"
fi
if [[ "$WOULD_REFORMAT" = "do_reformat" ]] ; then
set +e
out=$(php $DIR/.././vendor/bin/pint "$1")
retval="$?"
set -e
echo "$out"
exit "$retval"
fi
exit 255

47
gofmt.sh Executable file
View File

@@ -0,0 +1,47 @@
#!/bin/bash
set -uo pipefail
IFS=$'\n\t'
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$DIR/_reformat-common.bash"
set -e
file="$1"
pushd "$(dirname "$file")" 2>&1
if [[ "$WOULD_REFORMAT" = "would_reformat" ]]; then
would_reformat_retval=1
syntax_error_retval=2
set +e
out=$(gofmt -l "$file")
exitval="$?"
set -e
if [[ "$exitval" = "$syntax_error_retval" ]]; then
retval="$exitval"
elif [[ "$out" = "$file" ]]; then
retval="$would_reformat_retval"
else
# success
retval="$exitval"
fi
echo "$out"
exit "$retval"
fi
if [[ "$WOULD_REFORMAT" = "do_reformat" ]]; then
out=$(gofmt -w "$file" 2>&1 >/dev/null)
retval="$?"
echo "$out"
exit "$retval"
fi
exit 255

View File

@@ -3,38 +3,43 @@
set -uo pipefail
IFS=$'\n\t'
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
here=$(pwd)
pushd "$DIR" || exit 255
rm -f .root
pwd >.root
popd || exit 255
ret=0
# If this use of readlink breaks, see
# https://unix.stackexchange.com/questions/47710/find-only-destination-of-symlink
# for discussion of alternatives.
for i in _reformat-common.sh do-reformat.sh would-reformat.sh; do
if [[ -f "$here"/bin/"$i" ]] ; then
dest=$(readlink bin/$i)
if [[ x"$dest" == x"$DIR/$i" ]] ; then
echo $i is in place, good
for i in _reformat-common.bash do-reformat.sh would-reformat.sh _wrflog.sh .root; do
if [[ -f "$here"/bin/"$i" ]]; then
dest=$(readlink bin/"$i")
if [[ "$dest" == "$DIR/$i" ]]; then
echo "$i" is in place, good
else
echo $i exists but it is not a symlink or does not point to the right place
echo If it is a symlink, it points to: $dest
echo "$i" exists but it is not a symlink or does not point to the right place
echo If it is a symlink, it points to: "$dest"
echo "(If it doesn't point anywhere, then it probably is not a symlink)"
ret=1
fi
else
# it doesn't exist; create it
echo setting $i up
mkdir -p $here/bin
ln -s $DIR/$i $here/bin/$i
echo setting "$i" up
mkdir -p "$here"/bin
ln -s "$DIR"/"$i" "$here"/bin/"$i"
fi
done
if [[ -f $here/.dir-locals.el ]] ; then
if [[ -f $here/.dir-locals.el ]]; then
dest=$(readlink .dir-locals.el)
if [[ x"$dest" == x"$DIR/_dir-locals.el" ]] ; then
if [[ "$dest" == "$DIR/_dir-locals.el" ]]; then
echo .dir-locals.el is in place, good
else
echo .dir-locals.el exists but it is not a symlink or does not point to
@@ -46,7 +51,7 @@ if [[ -f $here/.dir-locals.el ]] ; then
fi
else
echo setting .dir-locals.el up
ln -s $DIR/_dir-locals.el $here/.dir-locals.el
ln -s "$DIR"/_dir-locals.el "$here"/.dir-locals.el
fi
exit $ret

46
isort-and-black.sh Executable file
View File

@@ -0,0 +1,46 @@
#!/bin/bash
set -uo pipefail
IFS=$'\n\t'
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$DIR/_reformat-common.bash"
file="$1"
if [[ "$WOULD_REFORMAT" = "would_reformat" ]]; then
out1=$(pipx run isort "$file" 2>/dev/null)
c1="$?"
if [[ "$c1" != "0" ]]; then
# would reorder imports
if [[ "$c1" = "1" ]]; then
exit 1
fi
# syntax error; 123 is normalized to 2
if [[ "$c1" = "123" ]]; then
exit 2
fi
# some other problem that we don't know how to handle
exit 255
fi
out2=$(pipx run black --check "$file" 2>/dev/null)
c2="$?"
printf '%s\n%s' "$out1" "$out2"
exit "$c2"
fi
if [[ "$WOULD_REFORMAT" = "do_reformat" ]]; then
out1=$(pipx run black "$1" 2>/dev/null && pipx run isort "$file" 2>/dev/null)
c="$?"
printf '%s\n' "$out1"
exit "$c"
fi
exit 255

32
prettier.sh Executable file
View File

@@ -0,0 +1,32 @@
#!/bin/bash
set -uo pipefail
IFS=$'\n\t'
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$DIR/_reformat-common.bash"
set -e
file="$1"
pushd "$(dirname "$file")" 2>&1
if [[ "$WOULD_REFORMAT" = "would_reformat" ]]; then
set +e
out=$(npx prettier --check "$file" 2>&1 >/dev/null)
retval="$?"
set -e
exit "$retval"
fi
if [[ "$WOULD_REFORMAT" = "do_reformat" ]]; then
out=$(npx prettier --write "$file" 2>&1 >/dev/null)
retval="$?"
echo "$out"
exit "$retval"
fi
exit 255

50
rustfmt.sh Executable file
View File

@@ -0,0 +1,50 @@
#!/bin/bash
set -uo pipefail
IFS=$'\n\t'
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$DIR/_reformat-common.bash"
log "in rustfmt.sh"
set -e
file="$1"
log what
pushd "$(dirname "$file")" 2>&1
if [[ "$WOULD_REFORMAT" = "would_reformat" ]]; then
log beavis
set +e
out=$(rustfmt --check "$file" 2>&1 >/dev/null)
retval="$?"
set -e
log got back retval $retval
exit "$retval"
fi
if [[ "$WOULD_REFORMAT" = "do_reformat" ]]; then
log in do reformat mode
out=$(rustfmt "$file" 2>&1 >/dev/null)
retval="$?"
echo "$out"
log $out
exit "$retval"
fi
log are we here now
exit 255

39
shfmt.sh Normal file
View File

@@ -0,0 +1,39 @@
set -uo pipefail
IFS=$'\n\t'
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$DIR/_reformat-common.bash"
set -e
file="$1"
pushd "$(dirname "$file")" 2>&1
if [[ "$WOULD_REFORMAT" = "would_reformat" ]]; then
# probably others too :(
syntax_error_retval=1
set +e
out=$(dart format -o none --set-exit-if-changed "$file"
retval="$?"
set -e
if [[ "$retval" = "$syntax_error_retval" ]]; then
retval=2
fi
exit "$retval"
fi
if [[ "$WOULD_REFORMAT" = "do_reformat" ]]; then
out=$(shfmt -w "$file" 2>&1 > /dev/null)
retval="$?"
echo "$out"
exit "$retval"
fi
exit 255

14
tests/ongoing.sh Executable file
View File

@@ -0,0 +1,14 @@
#!/bin/bash
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$DIR"/.. || exit
source ./_reformat-common.bash
setUp() {
cd "$DIR"/.. || exit
}
# shellcheck disable=SC1091
source shunit2

126
tests/project-root1.sh Executable file
View File

@@ -0,0 +1,126 @@
#!/bin/bash
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$DIR"/.. || exit
source ./_reformat-common.bash
setUp() {
cd "$DIR"/.. || exit
unset PROJECT_ROOT
unset WF_ROOT
}
testProjectRoot001() {
dir=$(mktemp -d --tmpdir would-reformat-testProjectRoot001.XXXXXX)
cd "$dir" || exit
git init >/dev/null 2>&1
echo 'abc123' >random-file
pr=$(project_root random-file)
assertEquals "$dir" "$pr"
}
# This won't work (yet) because our only method of determining a
# project root is to ask git what it thinks.
testProjectRoot002() {
dir=$(mktemp -d --tmpdir would-reformat-testProjectRoot001.XXXXXX)
cd "$dir" || exit
echo 'abc123' >random-file
pr=$(project_root random-file)
assertEquals "" "$pr"
}
testResolveSymlink001() {
d1=$(mktemp -d --tmpdir targetdir.XXXXX)
expected="$d1/target"
touch "$expected"
d2=$(mktemp -d --tmpdir linkdir.XXXXX)
link="$d2"/link
ln -s "$d1"/target "$link"
resolved=$(resolve_symlink "$link")
assertEquals "$expected" "$resolved"
}
# This test is ugly but there's no obvious way to make it
# better without making it entirely tautological.
testNoramlizeDirOfFile001() {
input="$DIR"/../install.sh
expected="$DIR/.."
out=$(normalize_dir_of_file "$input")
assertEquals "$expected" "$out/tests/.."
}
testWfRoot001() {
expected=$(normalize_dir_of_file "$DIR/../install.sh")
dir=$(mktemp -d --tmpdir would-reformat-testProjectRoot001.XXXXXX)
cd "$dir" || exit
git init >/dev/null 2>&1
"$DIR"/../install.sh 2>&2 >/dev/null
got=$(wf_root)
assertEquals "$expected" "$got"
}
testWfRoot002() {
expected=$(normalize_dir_of_file "$DIR/../install.sh")
export WF_ROOT="$expected"
got=$(wf_root)
assertEquals "$expected" "$got"
}
testNoCustomSniffer001() {
dir=$(mktemp -d --tmpdir would-reformat-testProjectRoot001.XXXXXX)
cd "$dir" || exit
git init >/dev/null 2>&1
"$DIR"/../install.sh 2>&2 >/dev/null
touch perl-file.pl
touch typescript-file.ts
out1=$(sniff_file_type "$(pwd)"/perl-file.pl)
out2=$(sniff_file_type "$(pwd)"/typescript-file.ts)
assertEquals "perl" "$out1"
assertEquals "typescript" "$out2"
}
testCustomSniffer001() {
dir=$(mktemp -d --tmpdir would-reformat-testProjectRoot001.XXXXXX)
cd "$dir" || exit
git init >/dev/null 2>&1
"$DIR"/../install.sh 2>&2 >/dev/null
mkdir -p .would-reformat || exit 1
cp "$(wf_root)"/examples/custom-sniffer ./.would-reformat/custom-sniffer || exit 1
chmod +x ./.would-reformat/custom-sniffer || exit 1
touch prolog-file.pl
touch qt-translation-file.ts
out1=$(sniff_file_type "$(pwd)"/prolog-file.pl)
out2=$(sniff_file_type "$(pwd)"/qt-translation-file.ts)
assertEquals "prolog" "$out1"
assertEquals "qt-translation" "$out2"
}
# shellcheck disable=SC1091
source shunit2

View File

@@ -1,85 +1,59 @@
#!/bin/bash
set -uo pipefail
set -euo pipefail
IFS=$'\n\t'
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
## This should be rarely if ever necessary, but here it is:
# source "$DIR/_wrflog.sh"
# wrflog before sourcing common funcs, I think i am in dir: $(pwd)
source "$DIR/_reformat-common.bash"
#export LOG_DEST="$LOG_TO"
log "is this thing on"
log i think it is on
file="$1"
PROJECT_ROOT=$(project_root "$file")
export PROJECT_ROOT="$PROJECT_ROOT"
WF_ROOT=$(wf_root)
export WF_ROOT="$WF_ROOT"
source "$DIR/_reformat-common.sh"
## fixme use realpath or something on "$file"
file_type=$(sniff_file_type $file)
file_type=$(sniff_file_type "$file")
if [[ $file_type = "javascript" || \
$file_type = "vue" || \
$file_type = "typescript" || \
$file_type = "css" || \
$file_type = "php" || \
$file_type = "html" || \
$file_type = "jsx" || \
$file_type = "tsx" || \
$file_type == "css" || \
$file_type == "scss" || \
$file_type = "sh" ]]; then
out=$(npx prettier --check $file 2>&1 >/dev/null)
retval="$?"
success_retval=0
would_reformat_retval=1
syntax_error_retval=2
elif [[ $file_type == "python" ]]; then
export WOULD_REFORMAT=would_reformat
out=$(pipx run isort --profile black --check $file 2>/dev/null)
isort_retval="$?"
wrapper=$(choose_wrapper "$file_type")
success_retval=0
would_reformat_retval=1
syntax_error_retval=123
log wrapper is "$wrapper"
if [[ x"$isort_retval" = x"0" ]] ; then
out=$(pipx run black --check $file 2>/dev/null)
retval="$?"
else
retval="$isort_retval"
fi
elif [[ $file_type == "golang" ]] ; then
success_retval=0
would_reformat_retval=1
syntax_error_retval=2
set +e
echo asdf
out=$("$wrapper" "$file" 2>&1 >/dev/null)
retval="$?"
echo fdsa
out=$(gofmt -l $file)
exitval="$?"
set -e
if [[ x"$exitval" = x"$syntax_error_retval" ]] ; then
retval="$exitval"
elif [[ x"$out" = x"$file" ]] ; then
retval="$would_reformat_retval"
else
# success
retval="$exitval"
fi
elif [[ $file_type == "dart" ]] ; then
success_retval=0
would_reformat_retval=1
# probably others too :(
syntax_error_retval=65
out=$(dart format -o none --set-exit-if-changed $file)
exitval="$?"
retval="$exitval"
else
echo -n "ignoring"
exit 0
echo retval "$retval"
fi
success_retval=0
would_reformat_retval=1
syntax_error_retval=2
if [[ x"$retval" == x"$success_retval" ]]; then
if [[ "$retval" == "$success_retval" ]]; then
echo -n "would not change"
exit 0
elif [[ x"$retval" == x"$would_reformat_retval" ]]; then
elif [[ "$retval" == "$would_reformat_retval" ]]; then
echo -n "would change"
exit 1
elif [[ x"$retval" == x"$syntax_error_retval" ]]; then
elif [[ "$retval" == "$syntax_error_retval" ]]; then
echo -n "syntax error"
echo
echo "$out"