Files
would-reformat/CUSTOMIZATION.md
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

6.7 KiB

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:

#!/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.)

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

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:

#!/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.)

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.