Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
90 commits
Select commit Hold shift + click to select a range
3769ffb
ignore no-paren, no-body function heads. closes #185
novaugust Aug 7, 2024
1661f41
minor optimization
novaugust Aug 7, 2024
b3ccc02
Wrap up 1.0.0 docs overhaul
novaugust Aug 8, 2024
ea8c723
1.0.0
novaugust Aug 8, 2024
a9bc689
Add missing documentation. Closes #188
novaugust Aug 27, 2024
ee0a6fa
update readme link
novaugust Aug 27, 2024
c29d10d
add more verbiage re styler can add bugs. Closes #186
novaugust Aug 27, 2024
3a66e42
reword warning to list examples of breakages
novaugust Aug 27, 2024
f53cf7c
Update styles.md
ktekinay Aug 30, 2024
b4bca9c
Merge pull request #192 from ktekinay/feature/fix-style-example
novaugust Aug 30, 2024
f2b269e
Add Stream.run optimizations, fix optimizations shrinking pipes to on…
novaugust Sep 11, 2024
d6c90cd
fix infinite loop rewriting negated if with empty do body. closes #196
novaugust Sep 23, 2024
cde90d3
Remove unless from codebases (#194)
novaugust Sep 23, 2024
926ad51
use `not` over `!` when rewriting `unless a (> >= < <= in) b`
novaugust Sep 23, 2024
9125157
actually, lets only do `not` for `in`
novaugust Sep 23, 2024
42b5d40
v1.1.0
novaugust Sep 23, 2024
79dce09
update version in readme
novaugust Sep 23, 2024
4aa18ba
Don't pipe into `Kernel.!` when rewriting unless with pipes
novaugust Sep 23, 2024
7fb05fa
configs: improve comment handling when moving a small number of nodes…
novaugust Oct 18, 2024
b38b990
v1.1.2
novaugust Oct 18, 2024
548fbf6
pipes: improve comment behaviour in optimizations (Closes #176)
novaugust Oct 18, 2024
fe32a84
One-line unpiped assignments (#197)
novaugust Nov 7, 2024
60f79c7
Pipify: `d(a |> b |> c)` => `a |> b() |> c() |> d()`(#198)
novaugust Nov 7, 2024
5016027
1.18 hard deprecations (#203)
novaugust Nov 19, 2024
929d217
docs and changelog
novaugust Nov 19, 2024
da3f3c9
docs prep for 1.2
novaugust Nov 20, 2024
65007ba
v1.2.0
novaugust Nov 20, 2024
d930cab
correct docs on Enum.into
novaugust Nov 20, 2024
872ad34
Fix pipifying pipes-in-pipes (Closes #204)
novaugust Nov 21, 2024
4ec6ba7
v1.2.1
novaugust Nov 21, 2024
b7dcfd5
introduce `# styler:sort` comment directive (#205)
novaugust Nov 23, 2024
411cc93
styler:sort - handle tuples
novaugust Nov 23, 2024
b13d045
mix format :X
novaugust Nov 23, 2024
0e8485b
style:sort - dont dedupe
novaugust Nov 23, 2024
5b56613
docs and fix a typo
novaugust Nov 25, 2024
d35a28d
fiddle with private apis to ease iex playing
novaugust Nov 27, 2024
98c5c1c
remove Zipper.reduce/3 - bugged, but more importantly unused
novaugust Nov 27, 2024
cef6015
Maintain comment-node relations when applying `#styler:sort` directiv…
novaugust Jan 13, 2025
13bc947
v1.3.0
novaugust Jan 13, 2025
03b1ee6
correct changelog
novaugust Jan 13, 2025
d6a2a5e
fix twople bug in sort directive, add map sorting
novaugust Jan 13, 2025
30446bf
v1.3.1
novaugust Jan 13, 2025
5d3d620
defstruct with list literal
novaugust Jan 13, 2025
bef00c9
ci: update elixir and erlang versions
tfiedlerdejanze Jan 13, 2025
d132d79
sort directive: sort values of keys. Closes #208
novaugust Jan 13, 2025
877007a
Merge pull request #209 from tfiedlerdejanze/ci-elixir-patch-upgrades
novaugust Jan 13, 2025
88d3334
v1.3.2
novaugust Jan 14, 2025
9b0207b
fix comments bug in styler:sort directive
novaugust Jan 15, 2025
efb2cb9
styler:sort arbitrary ast within do end blocks
novaugust Jan 16, 2025
9983bf2
improve `with` statement replacements
novaugust Jan 21, 2025
470b390
v1.3.3
novaugust Jan 21, 2025
7932147
alias lifting: shrink when alias already exists. closes #201
novaugust Jan 24, 2025
ff7755d
alias lifting: be better about conflicts. Closes #193
novaugust Jan 24, 2025
3750e0c
improve alias lift collision case
novaugust Jan 24, 2025
fc71aee
remove vestigial with rewriting head
novaugust Jan 25, 2025
9404d5f
pipes: handle pipifying functions whose first arg is itself a pipe. c…
novaugust Feb 13, 2025
ff004ca
cleanup the messes left in the previous commit 🙈
novaugust Feb 13, 2025
5a23833
correct issue number in change log
novaugust Feb 13, 2025
297106a
test against 1.18
novaugust Feb 20, 2025
ee34edd
1.18 warnings + formatting
novaugust Feb 20, 2025
8d921d9
no one saw that right?
novaugust Feb 20, 2025
e083b4b
ex1.17+: replace `:timer.units(x)` with the new `to_timeout(unit: x)`…
novaugust Feb 20, 2025
d0ecf1d
1.18+: change struct updates to map updates. Closes #199
novaugust Feb 20, 2025
74d6fd2
ensure test works across versions
novaugust Feb 20, 2025
fc6fb5d
fix `with` rewrites when keyword with an else stab (#220)
novaugust Feb 20, 2025
a46c43f
pipify nested function calls with pipe as the first argument. closes …
novaugust Feb 20, 2025
ceb827a
change struct update deprecation to ex1.19+
novaugust Feb 20, 2025
d015a99
docs docs docs docs docs!
novaugust Jan 25, 2025
6896d97
ship struct update to map update changes after all
novaugust Feb 20, 2025
aaedd0c
v1.4.0
novaugust Feb 20, 2025
3d5a485
Add OTP26/27 but only run for 1.17/1.18
halfdan Feb 21, 2025
d3318fd
Merge pull request #222 from halfdan/expand-matrix
novaugust Feb 21, 2025
941d067
fix `with` redundant body + non-arrow behind redundant clause. Closes…
novaugust Feb 21, 2025
9990eb6
link to quokka
novaugust Feb 24, 2025
3b0571e
rewrite `to_timeout(unit: x * m)` to use larger units in some cases
novaugust Mar 3, 2025
8fe1ca0
defs test describe formatting
novaugust Mar 3, 2025
13320e9
dont crash on invalid defs
novaugust Mar 3, 2025
1df5f1d
fix CI for older elixir
novaugust Mar 6, 2025
be4dcec
v1.4.1
novaugust Mar 17, 2025
6b42462
if: drop empty do bodies. Closes #227
novaugust Apr 1, 2025
5b1c946
Fix large comment block mangling bug when ordering sibling AST (#232)
novaugust Apr 29, 2025
50ae386
if: treat is_nil as a negator
novaugust Apr 29, 2025
17434b6
Revert "if: treat is_nil as a negator"
novaugust Apr 29, 2025
aa3e7ce
Revert "if: drop empty do bodies. Closes #227"
novaugust Apr 29, 2025
c511610
v1.4.2
novaugust May 1, 2025
f66fc54
changelog: fix GH md formatting issues
novaugust May 8, 2025
e2d4e11
optimize zipper performance
novaugust May 22, 2025
93dc8e4
Merge remote-tracking branch 'upstream/main' into 1.4.2-upstream-merge
JesseHerrick Jun 6, 2025
52c7e4a
We still don't want moduledoc false in modules without it
JesseHerrick Jun 6, 2025
cb50d8b
Remove missed conflict
JesseHerrick Jun 6, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,13 @@ jobs:
name: Ex${{matrix.elixir}}/OTP${{matrix.otp}}
strategy:
matrix:
elixir: ['1.15.7', '1.16.0', '1.17.0-rc.0']
otp: ['25.1.2']
elixir: ["1.15.8", "1.16.3", "1.17.3", "1.18.2"]
otp: ["25.3.2", "26.2.5", "27.2.4"]
exclude:
- elixir: "1.15.8"
otp: "27.2.4"
- elixir: "1.16.3"
otp: "27.2.4"
steps:
- uses: actions/checkout@v4
- uses: erlef/setup-beam@v1
Expand Down
4 changes: 2 additions & 2 deletions .tool-versions
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
erlang 26.1.2
elixir 1.16.0-otp-26
elixir 1.18.2-otp-27
erlang 27.2.3
225 changes: 225 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,231 @@
they can and will change without that change being reflected in Styler's semantic version.
## main

## 1.4.2

### Fixes

- fix comment misplacement for large comment blocks in config files and `# styler:sort` (#230, h/t @cschmatzler)

## 1.4.1

### Improvements

- `to_timeout/1` rewrites to use the next largest unit in some simple instances

```elixir
# before
to_timeout(second: 60 * m)
to_timeout(day: 7)
# after
to_timeout(minute: m)
to_timeout(week: 1)
```

### Fixes

- fixed styler raising when encountering invalid function definition ast

## 1.4.0

- A very nice change in alias lifting means Styler will make sure that your code is _using_ the aliases that it's specified.
- Shoutout to the smartrent folks for finding pipifying recursion issues
- Elixir 1.17 improvements and fixes
- Elixir 1.19-dev: delete struct updates

Read on for details.

### Improvements

#### Alias Lifting

This release taught Styler to try just that little bit harder when doing alias lifting.

- general improvements around conflict detection, lifting in more correct places and fewer incorrect places (#193, h/t @jsw800)
- use knowledge of existing aliases to shorten invocations (#201, h/t me)

example:

alias A.B.C

A.B.C.foo()
A.B.C.bar()
A.B.C.baz()

becomes:

alias A.B.C

C.foo()
C.bar()
C.baz()

#### Struct Updates => Map Updates

1.19 deprecates struct update syntax in favor of map update syntax.

```elixir
# This
%Struct{x | y}
# Styles to this
%{x | y}
```

**WARNING** Double check your diffs to make sure your variable is pattern matching against the same struct if you want to harness 1.19's type checking features. Apologies to folks who hoped Styler would do this step for you <3 (#199, h/t @SteffenDE)

#### Ex1.17+

- Replace `:timer.units(x)` with the new `to_timeout(unit: x)` for `hours|minutes|seconds` (This style is only applied if you're on 1.17+)

### Fixes

- `pipes`: handle pipifying when the first arg is itself a pipe: `c(a |> b, d)` => `a |> b() |> c(d)` (#214, h/t @kybishop)
- `pipes`: handle pipifying nested functions `d(c(a |> b))` => `a |> b |> c() |> d` (#216, h/t @emkguts)
- `with`: fix a stabby `with` `, else: (_ -> :ok)` being rewritten to a case (#219, h/t @iamhassangm)

## 1.3.3

### Improvements

- `with do: body` and variations with no arrows in the head will be rewritten to just `body`
- `# styler:sort` will sort arbitrary ast nodes within a `do end` block:

Given:

# styler:sort
my_macro "some arg" do
another_macro :q
another_macro :w
another_macro :e
another_macro :r
another_macro :t
another_macro :y
end

We get

# styler:sort
my_macro "some arg" do
another_macro :e
another_macro :q
another_macro :r
another_macro :t
another_macro :w
another_macro :y
end

### Fixes

- fix a bug in comment-movement when multiple `# styler:sort` directives are added to a file at the same time

## 1.3.2

### Improvements

- `# styler:sort` can be used to sort values of key-value pairs. eg, sort the value of a single key in a map (Closes #208, h/t @ypconstante)

## 1.3.1

### Improvements

- `# styler:sort` now works with maps and the `defstruct` macro

### Fixes

- `# styler:sort` no longer blows up on keyword lists :X

## 1.3.0

### Improvements

#### `# styler:sort` Styler's first comment directive

Styler will now keep a user-designated list or wordlist (`~w` sigil) sorted as part of formatting via the use of comments. Elements of the list are sorted by their string representation.

The intention is to remove comments to humans, like `# Please keep this list sorted!`, in favor of comments to robots: `# styler:sort`. Personally speaking, Styler is much better at alphabetical-order than I ever will be.

To use the new directive, put it on the line before a list or wordlist.

This example:

```elixir
# styler:sort
[:c, :a, :b]

# styler:sort
~w(a list of words)

# styler:sort
@country_codes ~w(
en_US
po_PO
fr_CA
ja_JP
)

# styler:sort
a_var =
[
Modules,
In,
A,
List
]
```

Would yield:

```elixir
# styler:sort
[:a, :b, :c]

# styler:sort
~w(a list of words)

# styler:sort
@country_codes ~w(
en_US
fr_CA
ja_JP
po_PO
)

# styler:sort
a_var =
[
A,
In,
List,
Modules
]
```

## 1.2.1

### Fixes

* `|>` don't pipify when the call is itself in a pipe (aka don't touch `a |> b(c |> d() |>e()) |> f()`) (Closes #204, h/t @paulswartz)

## 1.2.0

### Improvements

* `pipes`: pipe-ifies when first arg to a function is a pipe. reach out if this happens in unstylish places in your code (Closes #133)
* `pipes`: unpiping assignments will make the assignment one-line when possible (Closes #181)
* `deprecations`: 1.18 deprecations
* `List.zip` => `Enum.zip`
* `first..last = range` => `first..last//_ = range`

### Fixes

* `pipes`: optimizations are less likely to move comments (Closes #176)

## 1.1.2

### Improvements

* Config Sorting: improve comment handling when only sorting a few nodes (Closes #187)

## 1.1.1

### Improvements
Expand Down
71 changes: 58 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,18 @@ You can learn more about the history, purpose and implementation of Styler from

## Features

- auto-fixes [many credo rules](docs/credo.md), meaning you can turn them off to speed credo up
- [keeps a strict module layout](docs/module_directives.md#directive-organization)
- alphabetizes module directives
- [extracts repeated aliases](docs/module_directives.md#alias-lifting)
- [makes your pipe chains pretty as can be](docs/pipes.md)
- pipes and unpipes function calls based on the number of calls
- optimizes standard library calls (`a |> Enum.map(m) |> Enum.into(Map.new)` => `Map.new(a, m)`)
- replaces strings with sigils when the string has many escaped quotes
- ... and so much more

[See our Rewrites documentation on hexdocs](https://hexdocs.pm/styler/styles.html)
Styler fixes a plethora of elixir style and optimization issues automatically as part of mix format.

[See Styler's documentation on Hex](https://hexdocs.pm/styler/styles.html) for the comprehensive list of its features.

The fastest way to see what all it can do you for you is to just try it out in your codebase... but here's a list of a few features to help you decide if you're interested in Styler:

- sorts and organizes `import`/`alias`/`require` and other [module directives](docs/module_directives.md)
- keeps lists, sigils, and even arbitrary code sorted with the `# styler:sort` [comment directive](docs/comment_directives.md)
- automatically creates aliases for repeatedly referenced modules names ([_"alias lifting"_](docs/module_directives.md#alias-lifting))
- optimizes pipe chains for [readability and performance](docs/pipes.md)
- rewrites strings as sigils when it results in fewer escapes
- auto-fixes [many credo rules](docs/credo.md), meaning you can spend less time fighting with CI

## Who is Styler for?

Expand All @@ -41,7 +42,7 @@ Add `:styler` as a dependency to your project's `mix.exs`:
```elixir
def deps do
[
{:styler, "~> 1.1", only: [:dev, :test], runtime: false},
{:styler, "~> 1.4", only: [:dev, :test], runtime: false},
]
end
```
Expand Down Expand Up @@ -75,10 +76,54 @@ Styler's only current configuration option is `:alias_lifting_exclude`, which ac

#### No Credo-Style Enable/Disable

Styler [will not add configuration](https://github.com/adobe/elixir-styler/pull/127#issuecomment-1912242143) for ad-hoc enabling/disabling of rewrites. Sorry! Its implementation simply does not support that approach. There are however many forks out there that have attempted this; please explore the [Github forks tab](https://github.com/adobe/elixir-styler/forks) to see if there's a project that suits your needs or that you can draw inspiration from.
Styler [will not add configuration](https://github.com/adobe/elixir-styler/pull/127#issuecomment-1912242143) for ad-hoc enabling/disabling of rewrites. Sorry!

However, Smartrent has a fork of Styler named [Quokka](https://github.com/smartrent/quokka) that allows for finegrained control of Styler. Maybe it's what you're looking for. If not, you can always fork it or Styler as a starting point for your own tool!

Ultimately Styler is @adobe's internal tool that we're happy to share with the world. We're delighted if you like it as is, and just as excited if it's a starting point for you to make something even better for yourself.

## WARNING: Styler can change the behaviour of your program!

In some cases, this can introduce bugs. It goes without saying, but look over your changes before committing to main :)

A simple example of a way Styler changes the behaviour of code is the following rewrite:

```elixir
# Before: this case statement...
case foo do
true -> :ok
false -> :error
end

# After: ... is rewritten by Styler to be an if statement!.
if foo do
:ok
else
:error
end
```

These programs are not semantically equivalent. The former would raise if `foo` returned any value other than `true` or `false`, while the latter blissfully completes.

However, Styler is about _style_, and the `if` statement is (in our opinion) of much better style. If the exception behaviour was intentional on the code author's part, they should have written the program like this:

```elixir
case foo do
true -> :ok
false -> :error
other -> raise "expected `true` or `false`, got: #{inspect other}"
end
```

Also good style! But Styler assumes that most of the time people just meant the `if` equivalent of the code, and so makes that change. If issues like this bother you, Styler probably isn't the tool you're looking for.

Other ways Styler can change your program:

- [`with` statement rewrites](https://github.com/adobe/elixir-styler/issues/186)
- [config file sorting](https://hexdocs.pm/styler/mix_configs.html#this-can-break-your-program)
- and likely other ways. stay safe out there!

>>>>>>> upstream/main
## Thanks & Inspiration

### [Sourceror](https://github.com/doorgan/sourceror/)
Expand Down
Loading