Skip to content

Releases: adobe/elixir-styler

v1.10.1: experimental refactoring tasks

12 Jan 21:18

Choose a tag to compare

Adds two experimental refactoring features as mix tasks.

mix styler.remove_unused

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.

mix styler.remove_unused will automate the removal of those unused require: statements, alongside any unused import: and unused alias: warnings.

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.

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.

mix styler.inline_attrs <file>

Inlines one-off module attributes that define literal values.

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!

It would probably be most useful as a refactor ability for a language server, but CLIs are a nice second place.

An example of a situation where it results in an improvement:

# Unnecessary indirection with single-use literal-value module attributes
defmodule A do
  @http_client_key :http_key
  @default_client MyHTTPClient

  def http_client, do: Application.get_env(:my_app, @http_client_key, @default_client)
end
# Much better! styler.inline_attrs will perform this refactor
defmodule A do
  def http_client, do: Application.get_env(:my_app, :http_key, MyHTTPClient)
end

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")

You've been warned =)

v1.10.0

02 Dec 21:26

Choose a tag to compare

Improvements

Two new standard-library pipe optimizations

  • enum |> Enum.map(fun) |> Enum.intersperse(separator) => Enum.map_intersperse(enum, separator, fun)
  • enum |> Enum.sort() |> Enum.reverse() => Enum.sort(enum, :desc)

And Req (the http client library) pipe optimizations, as detailed below

Req pipe optimizations

Req is a popular HTTP Client. If you aren't using it, you can just ignore this whole section!

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.

More succinctly, these two statements are equivalent:

  • foo |> Req.new() |> Req.merge(bar) |> Req.post!()
  • Req.post!(foo, bar)

Styler now rewrites the former to the latter, since "less is more" or "code is a liability".

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.

v1.9.1

01 Oct 13:14

Choose a tag to compare

Fix

  • fixes rewrites of single-clause case statement with assignment parent (Closes #247, h/t @vasspilka)

v1.9.0 - to_timeout with plural units

22 Sep 19:40

Choose a tag to compare

1.9.0

This was a weird one, but I found myself often writing to_timeout with plural units and then having to go back and fix 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, Styler will correct code that would otherwise produce a runtime error, saving you from flow-breaking backtracking.

Improvements

to_timeout improvements:

  • translate plural units to singular to_timeout(hours: 2) -> to_timeout(hour: 2) (plurals are valid ast, but invalid arguments to this function)
  • 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)

styler's LLM edition

11 Sep 16:43

Choose a tag to compare

Improvements

Rewrite single-clause case statements to be assignments (h/t 🤖)

# before
case foo |> Bar.baz() |> Bop.boop() do
  {:ok, widget} ->
    x = y
    wodget(widget)
end

# after
{:ok, widget} = foo |> Bar.baz() |> Bop.boop()
x = y
wodget(widget)

v1.7.0

12 Aug 20:44

Choose a tag to compare

1.7.0

Surprising how fast numbers go up when you're following semver.

Two new features, one being a pipe optimization and the other a style-consistency-enforcer in cond statements.

Improvements

  • |> Enum.filter(fun) |> List.first([default]) => |> Enum.find([default], fun) (#242, h/t @janpieper)

cond

If the last clause's left-hand-side is a truthy atom, map literal, or tuple, rewrite it to be true

# before
cond do
  a -> b
  c -> d
  :else -> e
end

# styled
cond do
  a -> b
  c -> d
  true -> e
end

This also helps Styler identify 2-clause conds that can be rewritten to if/else more readily, like the following:

# before
cond do
  a -> b
  :else -> c
end

# styled
if a do
  b
else
  c
end

v1.6.0: ExUnit assertions edition

28 Jul 20:15

Choose a tag to compare

That's right, a feature release again so soon!

Improvements

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.

Some of these rewrites are not semantically equivalent; for example, refute is_nil(false) will be rewritten to assert false, which will fail.

ExUnit assert/refute rewrites

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.

These changes are best summarized by the following table:

before styled
assert !x refute x
assert not x refute x
assert !!x assert x
assert x != nil assert x
assert x == nil no change
assert is_nil(x) no change
assert !is_nil(x) assert x
assert x not in y refute x in y
refute negated
refute x no change
refute !x assert x
refute not x assert x
refute x != y assert x == y
refute x !== y assert x === y
refute x != nil assert x == nil
refute x not in y assert x in y
refute comparison
refute x < y assert x >= y
refute x <= y assert x > y
refute x > y assert x <= y
refute x >= y assert x < y
  • assert Enum.member?(y, x) -> assert x in y
  • assert Enum.find(x, y) -> assert Enum.any?(x, y) (nb. not semantically equivalent in theory, but equivalent in practice)
  • assert Enum.any?(y, & &1 == x) -> assert x in y
  • assert Enum.any?(y, fn var -> var == x end) -> assert x in y

Fixes

  • 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)

v1.5.1

20 Jul 17:16

Choose a tag to compare

Fixes

  • alias lifting: handle comments in snippets with no existing directives (#239, h/t @kerryb)

v1.5.0

16 Jul 02:14

Choose a tag to compare

Improvements

  • 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)
  • added :minimum_supported_elixir_version configuration to better support libraries using Styler (#231, h/t @maennchen)
  • # styler:sort will now sort keys for struct/map typespecs (#213, h/t @rojnwa)

Fixes

  • apply alias lifting to snippets with no modules or module directives in them. (#189, @h/t @halfdan)
  • fix de-sugaring of syntax-sugared keyword lists whose values weren't atoms in map values (#236, h/t @RisPNG)
  • fix mix config sorting mangling floating comment blocks in some cases (#230 again, h/t @ryoung786)

v1.4.2

08 May 05:14

Choose a tag to compare

Fixes

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