Skip to content

Releases: adobe/elixir-styler

Improve config sorting comment handling

18 Oct 18:30
Compare
Choose a tag to compare

Improvements

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

v1.1.1

18 Oct 18:30
Compare
Choose a tag to compare

Improvements

  • unless: rewrite unless a |> b |> c as unless !(a |> b() |> c()) rather than unless a |> b() |> c() |> Kernel.!() (h/t @GregMefford)

v1.1.0

18 Oct 18:30
Compare
Choose a tag to compare

Improvements

The big change here is the rewrite/removal of unless due to unless "eventually" being deprecated. Thanks to @janpieper and @ypconstante for bringing this up in #190.

  • unless: rewrite all unless to if (#190)
  • pipes: optimize |> Stream.{each|map}(fun) |> Stream.run() to |> Enum.each(fun)

Fixes

  • pipes: optimizations reducing 2 pipes to 1 no longer squeeze all pipes onto one line (#180)
  • if: fix infinite loop rewriting negated if with empty do body if x != y, do: (), else: :ok (#196, h/t @itamm15)

v1.0.0

08 Aug 16:44
Compare
Choose a tag to compare

1.0.0

Styler's two biggest outstanding bugs have been fixed, both related to compilation breaking during module directive organization. One was references to aliases being moved above where the aliases were declared, and the other was similarly module directives being moved after their uses in module directives.

In both cases, Styler is now smart enough to auto-apply the fixes we recommended in the old Readme.

Other than that, a slew of powerful new features have been added, the neatest one (in the author's opinion anyways) being Alias Lifting.

Thanks to everyone who reported bugs that contributed to all the fixes released in 1.0.0 as well.

Improvements

Alias Lifting

Along the lines of Credo.Check.Design.AliasUsage, Styler now "lifts" deeply nested aliases (depth >= 3, ala A.B.C....) that are used more than once.

Put plainly, this code:

defmodule A do
  def lift_me() do
    A.B.C.foo()
    A.B.C.baz()
  end
end

will become

defmodule A do
  @moduledoc false
  alias A.B.C

  def lift_me do
    C.foo()
    C.baz()
  end
end

To exclude modules ending in .Foo from being lifted, add styler: [alias_lifting_exclude: [Foo]] to your .formatter.exs

Module Attribute Lifting

A long outstanding breakage of a first pass with Styler was breaking directives that relied on module attributes which Styler moved after their uses. Styler now detects these potential breakages and automatically applies our suggested fix, which is creating a variable before the module. This usually happened when folks were using a library that autogenerated their moduledocs for them.

In code, this module:

defmodule MyGreatLibrary do
  @library_options [...]
  @moduledoc make_pretty_docs(@library_options)
  use OptionsMagic, my_opts: @library_options

  ...
end

Will now be styled like so:

library_options = [...]

defmodule MyGreatLibrary do
  @moduledoc make_pretty_docs(library_options)
  use OptionsMagic, my_opts: unquote(library_options)

  @library_options library_options

  ...
end

Mix Config File Organization

Styler now organizes Mix.Config.config/2,3 stanzas according to erlang term sorting. This helps manage large configuration files, removing the "where should I put this" burden from developers AND helping find duplicated configuration stanzas.

See the moduledoc for Styler.Style.Configs for more.

Pipe Optimizations

  • Enum.into(x, []) => Enum.to_list(x)
  • Enum.into(x, [], mapper) => Enum.map(x, mapper)
  • a |> Enum.map(m) |> Enum.join() to map_join(a, m). we already did this for join/2, but missed the case for join/1
  • lhs |> Enum.reverse() |> Kernel.++(enum) => lhs |> Enum.reverse(enum)

with styles

  • remove with structure with no left arrows in its head to be normal code (#174)
  • with true <- x(), do: y => if x(), do: y (#173)

Everything Else

  • if/unless: invert if and unless with != or !==, like we do for ! and not #132
  • @derive: move @derive before defstruct|schema|embedded_schema declarations (fixes compiler warning!) #134
  • strings: rewrite double-quoted strings to use ~s when there's 4+ escaped double-quotes
    ("\"\"\"\"" -> ~s("""")) (Credo.Check.Readability.StringSigils) #146
  • Map.drop(foo, [single_key]) => Map.delete(foo, single_key) #161 (also in pipes)
  • Keyword.drop(foo, [single_key]) => Keyword.delete(foo, single_key) #161 (also in pipes)

Fixes

  • don't blow up on def function_head_with_no_body_nor_parens (#185, h/t @ypconstante)
  • fix with arrow replacement + redundant body removal creating invalid statements (#184, h/t @JesseHerrick)
  • allow Kernel unary ! and not as valid pipe starts (#183, h/t @nherzing)
  • fix Map.drop(x, [a | b]) registering as a chance to refactor to Map.delete
  • alias: expands aliases when moving an alias after another directive that relied on it (#137)
  • module directives: various fixes for unreported obscure crashes
  • pipes: fix a comment-shifting scenario when unpiping
  • Timex.now/1 will no longer be rewritten to DateTime.now!/1 due to Timex accepting a wider domain of "timezones" than the stdlib (#145, h/t @ivymarkwell)
  • with: skip nodes which (unexpectedly) do not contain a do body (#158, h/t @DavidB59)
  • then(&fun/1): fix false positives on arithmetic &1 + x / 1 (#164, h/t @aenglisc)

Breaking Changes

  • drop support for elixir 1.14
  • ModuleDirectives: group callback attributes (before_compile after_compile after_verify) with nondirectives (previously, were grouped with use, their relative order maintained). to keep the desired behaviour, you can make new use macros that wrap these callbacks. Apologies if this makes using Styler untenable for your codebase, but it's probably not a good tool for macro-heavy libraries.
  • sorting configs for the first time can change your configuration; see Mix Configs docs for more

v1.0.0-rc.1

12 Jun 23:00
Compare
Choose a tag to compare

Lots of documentation added. Module Directives, Simple Style, and Control Flow are fleshed out. Pipes, Configs, and the manifesto prelude to control flow all need work still.

New Styles

  • Enum.into(x, []) => Enum.to_list(x)
  • Enum.into(x, [], mapper) => Enum.map(x, mapper)
  • a |> Enum.map(m) |> Enum.join() to map_join(a, m). we already did this for join/2, but missed the case for join/1

v1.0.0-rc.0

20 May 18:50
Compare
Choose a tag to compare

Please see the CHANGELOG for all 1.0.0 changes

At this point, 1.0.0 feels feature complete. Two things remains for a full release:

  1. feedback!
  2. documentation overhaul! monitor progress here

I believe it's safe to start using the 1.0 branch on your codebase. (We've been using it at Adobe since the alpha without issue). I'm releasing as an RC to reserve the ability to make a breaking change before shipping the final product, but I believe no additional breaking changes will occur and 1.0 will ship once I've wrapped documentation updates.

v1.0.0-alpha.0

18 Apr 20:27
Compare
Choose a tag to compare

v0.11.9

26 Jan 20:10
Compare
Choose a tag to compare

Improvements

  • pipes: check for Stream.foo equivalents to Enum.foo in a few more cases

Fixes

  • pipes: |> then(&(&1 op y)) rewrites with |> Kernel.op(y) as long as the operator is defined in Kernel; skips the rewrite otherwise (h/t @kerryb for the report & @saveman71 for the fix)

v0.11.8

25 Jan 19:40
Compare
Choose a tag to compare

Two releases in one day!? @koudelka made too good a point about Map.new having some siblings we were ignoring

Improvements

  • pipes: treat MapSet.new and Keyword.new the same way we do Map.new (h/t @koudelka)

v0.11.7

25 Jan 18:14
Compare
Choose a tag to compare

Improvements

  • deprecations: ~R -> ~r, Date.range/2 -> Date.range/3 with decreasing dates (h/t @milmazz)
  • if: rewrite if not x, do: y => unless x, do: y
  • pipes: |> Enum.map(foo) |> Map.new() => |> Map.new(foo)
  • pipes: remove unnecessary then/2 on named function captures: |> then(&foo/1) => |> foo(), |> then(&foo(&1, ...)) => |> foo(...) (thanks to @tfiedlerdejanze for the idea + impl!)