From 1d1542e62fc03d3271dbbbcf10df2edd40f9455c Mon Sep 17 00:00:00 2001 From: Matt Enlow Date: Wed, 12 Jun 2024 15:23:32 -0700 Subject: [PATCH] module directives --- README.md | 25 +++++++- docs/module_directives.md | 116 +++++++++++++++++++++++++++++++++++++- 2 files changed, 137 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 986dfd7..a193297 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,30 @@ you what's wrong, it just rewrites the code for you to fit its style rules. You can learn more about the history, purpose and implementation of Styler from our talk: [Styler: Elixir Style-Guide Enforcer @ GigCity Elixir 2023](https://www.youtube.com/watch?v=6pF8Hl5EuD4) -[See our Rewrites documentation on hexdocs for details on what all Styler does](https://hexdocs.pm/styler/) +## Features + +- auto-fixes [many credo rules](docs/credo.md), meaning you can turn them off to speed credo up +- [keeping a strict module layout](docs/module_directives.md#directive-organization) +- alphabetizing module directives +- [extracting repeated aliases](docs/moduledirectives.md#alias-lifting) +- piping and unpiping function calls based on the number of functons +- optimizing standard library calls (`a |> Enum.map(m) |> Enum.into(Map.new)` => `Map.new(a, m)`) +- using sigils for strings with many escaped quotes `\"` +- ... and so many other things. + +[See our Rewrites documentation on hexdocs for all the nitty-gritty on what all Styler does](https://hexdocs.pm/styler/) + +## Who is Styler for? + +Styler was designed for a large team (40+ engineers) working in a single codebase. It helps remove fiddly code review comments and removes failed linter CI slowdowns, helping teams get things done faster. Teams in similar situations might appreciate Styler. + +Its automations are also extremely valuable for taming legacy elixir codebases or just refactoring in general. Some of its rewrites have inspired code actions in elixir language servers. + +Conversely, Styler probably _isn't_ a good match for: + +- libraries +- experimental, macro-heavy codebases +- small teams that don't want to think about code standards ## Installation diff --git a/docs/module_directives.md b/docs/module_directives.md index d772504..0c725c4 100644 --- a/docs/module_directives.md +++ b/docs/module_directives.md @@ -19,11 +19,11 @@ Adds `@moduledoc false` to modules without a moduledoc unless the module's name Expands `Module.{SubmoduleA, SubmoduleB}` to their explicit forms for ease of searching. ```elixir -# Given +# Before import Foo.{Bar, Baz, Bop} alias Foo.{Bar, Baz.A, Bop} -# Styled +# After import Foo.Bar import Foo.Baz import Foo.Bop @@ -46,9 +46,87 @@ Modules directives are sorted into the following order: * `require` (sorted alphabetically) * everything else (order unchanged) +### Before + +```elixir +defmodule Foo do + @behaviour Lawful + alias A.A + require A + + use B + + def c(x), do: y + + import C + @behaviour Chaotic + @doc "d doc" + def d do + alias X.X + alias H.H + + alias Z.Z + import Ecto.Query + X.foo() + end + @shortdoc "it's pretty short" + import A + alias C.C + alias D.D + + require C + require B + + use A + + alias C.C + alias A.A + + @moduledoc "README.md" + |> File.read!() + |> String.split("") + |> Enum.fetch!(1) +end +``` + +### After + ```elixir -defmodule OrganizeMe do +defmodule Foo do + @shortdoc "it's pretty short" + @moduledoc "README.md" + |> File.read!() + |> String.split("") + |> Enum.fetch!(1) + @behaviour Chaotic + @behaviour Lawful + use B + use A.A + + import A.A + import C + + alias A.A + alias C.C + alias D.D + + require A + require B + require C + + def c(x), do: y + + @doc "d doc" + def d do + import Ecto.Query + + alias H.H + alias X.X + alias Z.Z + + X.foo() + end end ``` @@ -65,3 +143,35 @@ alias Foo.Bar ``` ## Alias Lifting + +When a module with three parts is referenced two or more times, styler creates a new alias for that module and uses it. + +```elixir +# Given +require A.B.C + +A.B.C.foo() +A.B.C.bar() + +# Styled +alias A.B.C + +require C + +C.foo() +C.bar() +``` + +### Collisions + +Styler won't lift aliases that will collide with existing aliases, and likewise won't lift any module whose name would collide with a standard library name. + +You can specify additional modules to exlude from lifting via the `:alias_lifting_exclude` configuration option. For the example above, the following configuration would keep Styler from creating the `alias A.B.C` node: + +```elixir +# .formatter.exs +[ + plugins: [Styler], + styler: [alias_lifting_exclude: [:C]], +] +```