README.md

# rebar3_format

A rebar plugin for code formatting

## Build

    $ rebar3 compile

## Use

Add the plugin to your rebar config:

```erlang
{plugins, [rebar3_format]}
```

Then just call your plugin directly in an existing application:

    $ rebar3 format

This will format every Erlang file under `/src` by default. You can specify the directory/file to format as following:

    $ rebar3 format --files src/my_subdir/*.erl
    $ rebar3 format --files src/other_subdir/my_file.erl
    $ rebar3 format --files test/**/*.erl

To save the formatted files in a different directory you have to pass it as a parameter:

    $ rebar3 format --output formatted/

## Configuration

The plugin supports the following configuration options in the `format` section of `rebar.config`:

* `formatter` (`module()`):
    - This is the module that will dictate the style in which all the code will be formatted. It must implement the `rebar3_formatter` behavior. This project itself provides 2 formatters:
        + `otp_formatter`: Based on the default formatter that comes with Erlang/OTP (`erl_prettypr`), we only fixed some bugs but then respected the original format it produced in its entirety. This formatter only recognizes 2 options:
            * `paper`(`pos_integer()`):
                - Specifies the preferred maximum number of characters on any line, including indentation.
                - The default value is `80`.
            * `ribbon`(`pos_integer()`):
                - Specifies the preferred maximum number of characters on any line, not counting indentation.
                - The default value is `56`.
        + `default_formatter`: Our own default formatter, defining our personal criteria for how to stylize Erlang code. It admits all the options listed below.
    - The default value is `default_formatter`.
* `options` (`#{atom() => term()}`):
    - A map with a list of options that should be interpreted by the chosen `formatter`. The available keys are:
        + `encoding`(`none | epp:source_encoding()`):
            * Encoding to use when writing files.
            * The default value is `none`.
        + `paper`(`pos_integer()`):
            * Specifies the preferred maximum number of characters on any line, including indentation.
            * The default value is `100`.
        + `ribbon`(`pos_integer()`):
            * Specifies the preferred maximum number of characters on any line, not counting indentation.
            * The default value is `90`.
        + `break_indent`(`pos_integer()`):
            * Specifies the preferred number of characters to use to indent a line that "breaks" from the previous one (for instance, a clause body after a clause head).
            * The default value is `4`.
        + `sub_indent`(`pos_integer()`):
            * Specifies the preferred number of characters to use to indent a line that "follows" the current one (for instance, a long clause head or a long function application).
            * The default value is `2`.
        + `inline_attributes` (`all | none | {when_over, pos_integer()}`):
            * Specifies the desired behavior for inlining attributes with lists, like `-export`, `-export_type` and `-optional_callbacks`.
            * When this option is `all`, the formatter will try to fit as many items in each line as permitted by `paper` and `ribbon`.
            * When the flag is `none`, the formatter will place each item in its own line.
            * When the flag is `{when_over, N}` the formatter will work as `none` for lists with up to `N` elements, and it will inline longer lists.
            * The default value is `all`, i.e. always put as many functions/types on each row as possible.
        + `inline_items` (`all | none | {when_over, pos_integer()}`):
            * Specifies the desired behavior when the formatter needs to use multiple lines for a multi-item structure (i.e. tuple, list, map, etc.).
            * **NOTE:** If the formatter can put all items in the same row, it will do it, regardless of this configuration.
            * When this option is `all`, the formatter will try to fit as many items in each line as permitted by `paper` and `ribbon`.
            * When the flag is `none`, the formatter will place each item in its own line.
            * When the flag is `{when_over, N}` the formatter will work as `none` for lists with up to `N` elements, and it will inline longer lists.
            * The default value is `{when_over, 25}` to properly accommodate large binaries or lists.
        + `inline_clause_bodies` (`boolean()`):
            * Specifies if clause bodies (for `case`, `function`, etc. statements) should be placed in the same line as the clause heads if `paper` and `ribbon` allows it or if all bodies should be placed in the next line after their clause heads.
            * The default value is `false`.
        + `inline_expressions` (`boolean()`):
            * Specifies if sequential expressions in a clause should be placed in the same line if `paper` and `ribbon` allows it or if each expression should be placed in its own line.
            * The default value is `false`.
        + `preserve_empty_lines` (`boolean()`):
            * Specifies if blank lines between statements should be preserved when formatting.
            * Keep in mind that blank lines between clauses, between items in tuples, lists, etc, between attributes, and so on will not be affected by this configuration and therefore they'll be unconditionally removed.
            * This option is only used when `inline_expressions` is `false`.
            * If this option is `true`, one empty line will preserved for each group of empty lines that are placed between expressions in a clause.
            * The default value is `true`.
* `files` (`[file:filename_all()]`):
    - List of wildcard patterns representing the files that will be formatted by default (i.e. when not using `--files` on command line).
    - The default value is `["src/**/*.?rl"]`
* `ignore` (`[file:filename_all()]`):
    - List of wildcard patterns representing the files that the formatter will ignore when formatting.
    - Note that it will ignore the files set for formatting either with the `files` option or using `--files` in the command line if they match one of the given wildcards.
    - You can also ignore a specific file adding the attribute `-format(ignore)` in it.

### Per-File Configuration

You can tweak any of the formatter options for a particular file, using the `format` attribute in it, like this:

```erlang
-format(#{paper => 80}).
```

## Test

To test the plugin just run `rebar3 test`.
It will essentially run `rebar3 format` inside `test_app`.
Add modules with any "tricky" formatting you want to `test_app/src`, and push them to github _including_ the `after` results.
The `after` results can be tought as the **expected output** behaviour.

## Contribute

To contribute to rebar3_format, please refer to [CONTRIBUTING](CONTRIBUTING.md).