Skip to content

Commit 6961e36

Browse files
JesseHerricknovaugustktekinaytfiedlerdejanzehalfdan
authored
Upstream merge 1.11.0 (#11)
* ignore no-paren, no-body function heads. closes adobe#185 * minor optimization * Wrap up 1.0.0 docs overhaul * 1.0.0 * Add missing documentation. Closes adobe#188 * update readme link * add more verbiage re styler can add bugs. Closes adobe#186 * reword warning to list examples of breakages * Update styles.md Fixed example for "drop superfluous _..." * Add Stream.run optimizations, fix optimizations shrinking pipes to one line (Closes adobe#180) * fix infinite loop rewriting negated if with empty do body. closes adobe#196 * Remove unless from codebases (adobe#194) * use `not` over `!` when rewriting `unless a (> >= < <= in) b` * actually, lets only do `not` for `in` * v1.1.0 * update version in readme * Don't pipe into `Kernel.!` when rewriting unless with pipes * configs: improve comment handling when moving a small number of nodes. Closes adobe#187 * v1.1.2 * pipes: improve comment behaviour in optimizations (Closes adobe#176) * One-line unpiped assignments (adobe#197) Closes adobe#181 * Pipify: `d(a |> b |> c)` => `a |> b() |> c() |> d()`(adobe#198) Closes adobe#133 * 1.18 hard deprecations (adobe#203) * `List.zip` => `Enum.zip` * `first..last = range` => `first..last//_ = range` * docs and changelog * docs prep for 1.2 * v1.2.0 * correct docs on Enum.into * Fix pipifying pipes-in-pipes (Closes adobe#204) * v1.2.1 * introduce `# styler:sort` comment directive (adobe#205) closes adobe#167 * styler:sort - handle tuples * mix format :X * style:sort - dont dedupe * docs and fix a typo * fiddle with private apis to ease iex playing * remove Zipper.reduce/3 - bugged, but more importantly unused * Maintain comment-node relations when applying `#styler:sort` directive (adobe#207) Co-authored-by: Greg Mefford <gmefford@adobe.com> Closes adobe#167 * v1.3.0 * correct changelog * fix twople bug in sort directive, add map sorting * v1.3.1 * defstruct with list literal * ci: update elixir and erlang versions * sort directive: sort values of keys. Closes adobe#208 * v1.3.2 * fix comments bug in styler:sort directive * styler:sort arbitrary ast within do end blocks * improve `with` statement replacements * v1.3.3 * alias lifting: shrink when alias already exists. closes adobe#201 * alias lifting: be better about conflicts. Closes adobe#193 * improve alias lift collision case * remove vestigial with rewriting head * pipes: handle pipifying functions whose first arg is itself a pipe. closes adobe#193 * cleanup the messes left in the previous commit 🙈 * correct issue number in change log * test against 1.18 * 1.18 warnings + formatting * no one saw that right? * ex1.17+: replace `:timer.units(x)` with the new `to_timeout(unit: x)` for `hours|minutes|seconds` Closes adobe#218 * 1.18+: change struct updates to map updates. Closes adobe#199 * ensure test works across versions * fix `with` rewrites when keyword with an else stab (adobe#220) Closes adobe#219 fixes for both <1.17 and >=1.17 * pipify nested function calls with pipe as the first argument. closes adobe#216 * change struct update deprecation to ex1.19+ * docs docs docs docs docs! * ship struct update to map update changes after all * v1.4.0 * Add OTP26/27 but only run for 1.17/1.18 * fix `with` redundant body + non-arrow behind redundant clause. Closes adobe#221 * link to quokka * rewrite `to_timeout(unit: x * m)` to use larger units in some cases * defs test describe formatting * dont crash on invalid defs * fix CI for older elixir * v1.4.1 * if: drop empty do bodies. Closes adobe#227 * Fix large comment block mangling bug when ordering sibling AST (adobe#232) Closes adobe#230 * if: treat is_nil as a negator * Revert "if: treat is_nil as a negator" This reverts commit 50ae386. * Revert "if: drop empty do bodies. Closes adobe#227" This reverts commit 6b42462. * v1.4.2 * changelog: fix GH md formatting issues * optimize zipper performance * Revert "optimize zipper performance" This reverts commit e2d4e11. * fix bug in unpiping syntax-sugared non-atom-valued keyword lists closes adobe#236 * add `minimum_supported_elixir_version` configuration. closes adobe#231 * Add license preambles to four files * feat: Apply aliases (adobe#237) Closes adobe#235 * fix: config sorting mangling floating comment blocks. Closes adobe#230 * styler:sort sorts struct/map based typespec keys. Closes adobe#213 * lift aliases in snippets. closes adobe#189 * inline module attribute * v1.5.0 * docs updates * more docs * ahem. nothing to see here. * fix a -> @ typo in module directive "interesting?" check * fix: comment mangling when lifting aliases in snippets. closes adobe#239 * clean up comment - that "bug" isnt a bug * v1.5.1 * fix: alias lifting snippets with non-block root. closes adobe#240 * rewrite negated assert/refute * assert Enum.foo rewrites, and tweaks to the negated rewrites * fix link in docs * v1.6.0 * cond: standardize on `true` for the literal in the final clause Co-authored-by: Adam Kittelson <akittelson@adobe.com> * consolidate config integration tests * Enum.filter(fun) |> List.first([default]) => Enum.find([default], fun) Closes adobe#242 * update pipes docs * more pipes docs * add some preaching around why styler does a weird thing * v1.7.0 * rewrite single-clause case statements to assignments * v1.8.0 * to_timeout: rewrite plurals to singulars * 😍 * one last bit of nerd * fix :timer.foo regression * all the work in one spot * v1.9.0 * add changelog link to hex package * readme updates * more readme tweakin * Fix rewrite of single-clause case statement with assignment parent. Closes adobe#247 * v1.9.1 * optimize Req pipes * less is more * tweak intro sentence * TIL capital sigils cant be escaped * sort |> reverse => sort(:desc) * allow docs for Styler.string_to_ast * Enum.map |> Enum.intersperse => Enum.map_intersperse * v1.10.0 * bump version in changelog * pipes docs reorganization * Add experimental mix styler.remove_unused task * Add `mix styler.inline_attrs <file>` refactor tool * v1.10.1 * `mix styler.remove_unused`: allow multiple file args * readme task link fix * fix inline_attributes task docs * module attributes: dont break references from use, moduledoc, etc * 1.11.0 * A few fixes for v1.11.0 --------- Co-authored-by: Matt Enlow <enlow@adobe.com> Co-authored-by: Kem Tekinay <ktekinay@mactechnologies.com> Co-authored-by: Matt Enlow <matt@novaugust.net> Co-authored-by: Theodor Fiedler <theo.fie@gmail.com> Co-authored-by: Fabian Becker <fbecker@adobe.com> Co-authored-by: Adam Kittelson <akittelson@adobe.com>
1 parent 463bcb5 commit 6961e36

39 files changed

Lines changed: 2496 additions & 715 deletions

.formatter.exs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
1+
# Copyright 2024 Adobe. All rights reserved.
2+
# This file is licensed to you under the Apache License, Version 2.0 (the "License");
3+
# you may not use this file except in compliance with the License. You may obtain a copy
4+
# of the License at http://www.apache.org/licenses/LICENSE-2.0
5+
6+
# Unless required by applicable law or agreed to in writing, software distributed under
7+
# the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
8+
# OF ANY KIND, either express or implied. See the License for the specific language
9+
# governing permissions and limitations under the License.
10+
111
[
212
inputs: [
313
"{mix,.formatter}.exs",
@@ -8,6 +18,6 @@
818
assert_style: 2
919
],
1020
plugins: [Styler],
11-
styler: [alias_lifting_exclude: []],
21+
styler: [alias_lifting_exclude: [], minimum_supported_elixir_version: nil],
1222
line_length: 122
1323
]

CHANGELOG.md

Lines changed: 246 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,254 @@
22

33
**Note** Styler's only public API is its usage as a formatter plugin. While you're welcome to play with its internals,
44
they can and will change without that change being reflected in Styler's semantic version.
5+
56
## main
67

8+
## 1.11.0
9+
10+
### Improvements
11+
12+
- `mix styler.inline_attrs`: Allow multiple file paths to be specified: `mix styler.inline_attrs <file1> [<file2> ...]`
13+
14+
#### Module Directive References
15+
16+
Module directives got smarter. Styler will no longer move module attributes below their references in `use` or `@moduledoc`s.
17+
18+
In other words, Styler will leave the following code untouched:
19+
20+
```elixir
21+
defmodule MyGreatLibrary do
22+
@library_options [...]
23+
@moduledoc make_pretty_docs(@library_options)
24+
use OptionsMagic, my_opts: @library_options
25+
end
26+
```
27+
28+
## 1.10.1
29+
30+
### Improvements
31+
32+
Adds two experimental refactoring features as mix tasks.
33+
34+
#### `mix styler.remove_unused`
35+
36+
With Elixir 1.20 on the horizon, many projects are about to discover that they have _a lot_ of unnecessary `require Logger` lines throughout their codebase.
37+
38+
`mix styler.remove_unused` will automate the removal of those `unused require:` statements, alongside any `unused import:` and `unused alias:` warnings.
39+
40+
This has long been an internal script useful for running after a bigger refactor that resulted in many superfluous aliases, but with 1.20 coming it seems it might be useful for others as well.
41+
42+
This will never be an integrated part of `Styler`'s format plugin features, as it would _not_ be correct to remove unused nodes whenever running format. It's typical to have unused warnings while in the midst of an implementation, and deleting that code would be obnoxious.
43+
44+
#### `mix styler.inline_attrs <file>`
45+
46+
Inlines one-off module attributes that define literal values.
47+
48+
This is something that sometimes is good, and sometimes is bad. In general, defining a module attribute when you could've just written an atom is bad, so inlining is good!
49+
50+
It would probably be most useful as a refactor ability for a language server, but CLIs are a nice second place.
51+
52+
An example of a situation where it results in an improvement:
53+
54+
```elixir
55+
# Unnecessary indirection with single-use literal-value module attributes
56+
defmodule A do
57+
@http_client_key :http_key
58+
@default_client MyHTTPClient
59+
60+
def http_client, do: Application.get_env(:my_app, @http_client_key, @default_client)
61+
end
62+
# Much better! styler.inline_attrs will perform this refactor
63+
defmodule A do
64+
def http_client, do: Application.get_env(:my_app, :http_key, MyHTTPClient)
65+
end
66+
```
67+
68+
It's worthwhile to run this on some suspicious files, then followup with manual intervention when it went too far. This style is not aware of quote boundaries, and so might do some broken things. (Hence "EXPERIMENTAL")
69+
70+
You've been warned =)
71+
72+
## 1.10.0
73+
74+
### Improvements
75+
76+
Two new standard-library pipe optimizations
77+
78+
- `enum |> Enum.map(fun) |> Enum.intersperse(separator)` => `Enum.map_intersperse(enum, separator, fun)`
79+
- `enum |> Enum.sort() |> Enum.reverse()` => `Enum.sort(enum, :desc)`
80+
81+
And Req (the http client library) pipe optimizations, as detailed below
82+
83+
#### Req pipe optimizations
84+
85+
[Req](https://github.com/wojtekmach/req) is a popular HTTP Client. If you aren't using it, you can just ignore this whole section!
86+
87+
Reqs 1-arity "execute the request" functions (`delete get head patch post put request run`) have a 2-arity version that takes a superset of the arguments `Req.new/1` does as its first argument, and the typical `options` keyword list as its second argument. And so, many places developers are calling a 1-arity function can be replaced with a 2-arity function.
88+
89+
More succinctly, these two statements are equivalent:
90+
91+
- `foo |> Req.new() |> Req.merge(bar) |> Req.post!()`
92+
- `Req.post!(foo, bar)`
93+
94+
Styler now rewrites the former to the latter, since "less is more" or "code is a liability".
95+
96+
It also rewrites `|> Keyword.merge(bar) |> Req.foo()` to `|> Req.foo(bar)`. **This changes the program's behaviour**, since `Keyword.merge` would overwrite existing values in all cases, whereas `Req` 2-arity functions intelligently deep-merge values for some keys, like `:headers`.
97+
98+
## 1.9.1
99+
100+
### Fix
101+
102+
- fixes rewrites of single-clause case statement with assignment parent (Closes #247, h/t @vasspilka)
103+
104+
## 1.9.0
105+
106+
This was a weird one, but I found myself often writing `to_timeout` with plural units and then having to go back and fix
107+
the code to be singular units instead. Polling a few colleagues, it seemed I wasn't alone in that mistake. So for the first time,
108+
Styler will correct code that would otherwise produce a runtime error, saving you from flow-breaking backtracking.
109+
110+
### Improvements
111+
112+
`to_timeout` improvements:
113+
114+
- translate plural units to singular `to_timeout(hours: 2)` -> `to_timeout(hour: 2)` (plurals are valid ast, but invalid arguments to this function)
115+
- transform when there are multiple keys: `to_timeout(hours: 24 * 1, seconds: 60 * 4)` -> `to_timeout(day: 1, minute: 4)`. **this can introduce runtime bugs** due to duplicate keys, as in the following scenario: `to_timeout(minute: 60, hours: 3)` -> `to_timeout(hour: 1, hour: 3)`
116+
117+
## 1.8.0
118+
119+
### Improvements
120+
121+
Rewrite single-clause case statements to be assignments (h/t 🤖)
122+
123+
```elixir
124+
# before
125+
case foo |> Bar.baz() |> Bop.boop() do
126+
{:ok, widget} ->
127+
x = y
128+
wodget(widget)
129+
end
130+
131+
# after
132+
{:ok, widget} = foo |> Bar.baz() |> Bop.boop()
133+
x = y
134+
wodget(widget)
135+
```
136+
137+
## 1.7.0
138+
139+
Surprising how fast numbers go up when you're following semver.
140+
141+
Two new features, one being a pipe optimization and the other a style-consistency-enforcer in `cond` statements.
142+
143+
### Improvements
144+
145+
- `|> Enum.filter(fun) |> List.first([default])` => `|> Enum.find([default], fun)` (#242, h/t @janpieper)
146+
147+
#### `cond`
148+
149+
If the last clause's left-hand-side is a truthy atom, map literal, or tuple, rewrite it to be `true`
150+
151+
```elixir
152+
# before
153+
cond do
154+
a -> b
155+
c -> d
156+
:else -> e
157+
end
158+
159+
# styled
160+
cond do
161+
a -> b
162+
c -> d
163+
true -> e
164+
end
165+
```
166+
167+
This also helps Styler identify 2-clause conds that can be rewritten to `if/else` more readily, like the following:
168+
169+
```elixir
170+
# before
171+
cond do
172+
a -> b
173+
:else -> c
174+
end
175+
176+
# styled
177+
if a do
178+
b
179+
else
180+
c
181+
end
182+
```
183+
184+
## 1.6.0
185+
186+
That's right, a feature release again so soon!
187+
188+
### Improvements
189+
190+
This version of Styler adds many readability improvements around ExUnit `assert` and `refute`, specifically when working with 1. negations or 2. some `Enum` stdlib functions.
191+
192+
Some of these rewrites are not semantically equivalent due to `refute` passing for both `nil` and `false`.
193+
194+
#### ExUnit assert/refute rewrites
195+
196+
Styler now inverts negated (`!, not`) assert/refute (eg `assert !x` => `refute x`) statements, and further inverts `refute` with boolean comparison operators (`refute x < y` => `assert x >= y`) because non-trivial refutes are harder to reason about \[ _citation needed_ ]. Asserting something is not nil is the same as just asserting that something, so that's gone too now.
197+
198+
These changes are best summarized by the following table:
199+
200+
| before | styled |
201+
|---------------------|-------------------|
202+
| `assert !x` | `refute x` |
203+
| `assert not x` | `refute x` |
204+
| `assert !!x` | `assert x` |
205+
| `assert x != nil` | `assert x` |
206+
| `assert x == nil` | _no change_ |
207+
| `assert is_nil(x)` | _no change_ |
208+
| `assert !is_nil(x)` | `assert x` |
209+
| `assert x not in y` | `refute x in y` |
210+
| refute negated | |
211+
| `refute x` | _no change_ |
212+
| `refute !x` | `assert x` |
213+
| `refute not x` | `assert x` |
214+
| `refute x != y` | `assert x == y` |
215+
| `refute x !== y` | `assert x === y` |
216+
| `refute x != nil` | `assert x == nil` |
217+
| `refute x not in y` | `assert x in y` |
218+
| refute comparison | |
219+
| `refute x < y` | `assert x >= y` |
220+
| `refute x <= y` | `assert x > y` |
221+
| `refute x > y` | `assert x <= y` |
222+
| `refute x >= y` | `assert x < y` |
223+
224+
- `assert Enum.member?(y, x)` -> `assert x in y`
225+
- `assert Enum.find(x, y)` -> `assert Enum.any?(x, y)` (nb. not semantically equivalent in theory, but equivalent in practice)
226+
- `assert Enum.any?(y, & &1 == x)` -> `assert x in y`
227+
- `assert Enum.any?(y, fn var -> var == x end)` -> `assert x in y`
228+
229+
### Fixes
230+
231+
- alias lifting: fix bug lifting in snippets with a single ast node at the root level (like a credo config file) (#240, h/t @defndaines)
232+
233+
## 1.5.1
234+
235+
### Fixes
236+
237+
- alias lifting: handle comments in snippets with no existing directives (#239, h/t @kerryb)
238+
239+
## 1.5.0
240+
241+
### Improvements
242+
243+
- apply aliases to code. if a module is aliased, and then later referenced with its full name, Styler will now shorten it to its alias. (#235, h/t me)
244+
- added `:minimum_supported_elixir_version` configuration to better support libraries using Styler (#231, h/t @maennchen)
245+
- `# styler:sort` will now sort keys for struct/map typespecs (#213, h/t @rojnwa)
246+
247+
### Fixes
248+
249+
- apply alias lifting to snippets with no modules or module directives in them. (#189, @h/t @halfdan)
250+
- fix de-sugaring of syntax-sugared keyword lists whose values weren't atoms in map values (#236, h/t @RisPNG)
251+
- fix mix config sorting mangling floating comment blocks in some cases (#230 again, h/t @ryoung786)
252+
7253
## 1.4.2
8254

9255
### Fixes

0 commit comments

Comments
 (0)