Pretty Arrows
A mix format plugin that lines up the parts of your code that are easiest to
read in columns: the arrows in with clauses, the options in Ecto field
declarations, and the arrows in one-line case clauses.
Installation
Add pretty_arrows to your dependencies in mix.exs:
def deps do
[
{:pretty_arrows, "~> 0.4.0", only: [:dev, :test], runtime: false}
]
end
Then fetch it:
mix deps.get
Setup
Add the plugin to the :plugins list in .formatter.exs:
[
plugins: [PrettyArrows],
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
]
mix format now aligns with clauses and Ecto field declarations.
Plugins
| Module | Aligns | Options |
|---|---|---|
PrettyArrows |
with clauses, Ecto fields, case clauses |
:with, :ecto, :case |
WithFormatter |
with clauses only |
:with |
EctoFieldFormatter |
Ecto fields only |
:ecto |
CaseFormatter |
case clauses only |
:case |
Use PrettyArrows for all three alignments. Use one of the others if you only
want that feature. Each plugin reads only its own section of :pretty_arrows.
Examples
with clauses
Input:
with a_result <- a_func(),
{:ok, b_result_with_a_longer_name} <- b_func(),
c <- c_func() do
{:ok, c}
end
Output, with the default padding: :left:
with a_result <- a_func(),
{:ok, b_result_with_a_longer_name} <- b_func(),
c <- c_func() do
{:ok, c}
end
Set padding: :right to pad after each pattern instead:
with a_result <- a_func(),
{:ok, b_result_with_a_longer_name} <- b_func(),
c <- c_func() do
{:ok, c}
end
[
plugins: [PrettyArrows],
pretty_arrows: [with: [padding: :right]]
]
Ecto fields
Input:
schema "users" do
field :id, :uuid
field :name, :string
field :nowish, :datetime
field :c, :enum
end
Output, with the defaults (parens: false, columns: 2):
schema "users" do
field :id, :uuid
field :name, :string
field :nowish, :datetime
field :c, :enum
end
Keep the parentheses around the arguments:
[
plugins: [PrettyArrows],
pretty_arrows: [ecto: [parens: true]]
]
Align more columns. With columns: 3, the options after the type line up too:
schema "users" do
field :id, :uuid, primary_key: true
field :MinCreated, MyType.Util.Type.Integer, primary_key: true
field :name, :string, null: false
end
[
plugins: [PrettyArrows],
pretty_arrows: [ecto: [columns: 3]]
]
Trailing # comments on a field line stay on that line, and comment lines
between fields don't break the alignment.
case clauses
Two or more one-line clauses in a case block get their -> arrows lined up.
Clauses whose body is on the following lines are left alone.
Input:
case my_fun() do
{:ok, result_a} -> MyMod.a_fun(result_a)
{:ok, results_for_b} -> MyMod.b_other_fun(results_for_b)
{:error, reason} -> MyMod.error_handler(reason)
end
Output:
case my_fun() do
{:ok, result_a} -> MyMod.a_fun(result_a)
{:ok, results_for_b} -> MyMod.b_other_fun(results_for_b)
{:error, reason} -> MyMod.error_handler(reason)
end
[
plugins: [PrettyArrows]
]
Use CaseFormatter as the plugin to get only this alignment. To keep PrettyArrows
but turn off case alignment, set pretty_arrows: [case: [enabled: false]].
Only code is aligned. Lines inside heredocs, arrows inside string literals, and
-> in fn bodies inside a clause don't count as clauses.
Configuration
PrettyArrows reads all of its options from a single :pretty_arrows key.
Every option is shown with its default:
[
plugins: [PrettyArrows],
pretty_arrows: [
with: [
enabled: true, # align `with` clauses
padding: :left # :left or :right
],
ecto: [
enabled: true, # align Ecto `field`s
parens: false, # keep parentheses around field arguments
columns: 2 # how many argument columns to align
],
case: [
enabled: true # align one-line `case` clause arrows
]
]
]
| Option | Default | Accepted values |
|---|---|---|
with: [enabled:] |
true |
true, false |
with: [padding:] |
:left |
:left, :right |
ecto: [enabled:] |
true |
true, false |
ecto: [parens:] |
false |
true, false |
ecto: [columns:] |
2 |
a positive integer |
case: [enabled:] |
true |
true, false |
Unknown options and invalid values raise an ArgumentError that names the
offending key, so a typo fails the format run instead of being ignored:
** (ArgumentError) unknown option(s) [:padding_typo] in pretty_arrows.with, expected one of [:enabled, :padding]
How it works
The plugin first runs the standard Code.format_string!/2 formatter, then
works on the formatted text one line at a time:
- For a
withclause, it finds thewithline and thedoline that closes it, then pads the clause patterns so the<-arrows line up. Nestedwithblocks are aligned separately. - For
caseblocks, it aligns the->of one-line clauses at the clause indentation. Heredocs and string literals are skipped. - For Ecto fields, it groups consecutive
fieldlines, including comment lines between them, and pads each argument into its column. If an aligned line would exceed 98 characters, the remaining options move to a continuation line.
Heredoc contents (""" blocks) are never aligned, so with, field, and
case text inside test fixtures is left as written.
Because it works on the text line by line, each clause or field must start on
its own line for it to align. Layouts that put several clauses on one line are
formatted by mix format but not aligned.
Migrating from 0.2.x
Version 0.3.0 moves every option under :pretty_arrows and removes the old
spellings. Old top-level keys raise an error that points here.
| 0.2.x | 0.3.0 |
|---|---|
with_padding: :right |
pretty_arrows: [with: [padding: :right]] |
with: false |
pretty_arrows: [with: [enabled: false]] |
ecto: false |
pretty_arrows: [ecto: [enabled: false]] |
ecto: [parens: true] |
pretty_arrows: [ecto: [parens: true]] |
ecto: [columns: 3] |
pretty_arrows: [ecto: [columns: 3]] |
prettyarrows: [with: [padding: :right], ecto: [parens: true]] |
pretty_arrows: [with: [padding: :right], ecto: [parens: true]] |
ecto: [fields: [parens: true]] |
pretty_arrows: [ecto: [parens: true]] |
String values such as padding: "right" and enabled: "false" are no longer
accepted. Use atoms and booleans.
License
MIT