diff --git a/docs/module_directives.cheatmd b/docs/module_directives.cheatmd new file mode 100644 index 0000000..c2cbc05 --- /dev/null +++ b/docs/module_directives.cheatmd @@ -0,0 +1 @@ +# Module Directives (`use`, `import`, `alias`, `require`, ...) diff --git a/docs/pipe_chains.cheatmd b/docs/pipe_chains.cheatmd new file mode 100644 index 0000000..43e0350 --- /dev/null +++ b/docs/pipe_chains.cheatmd @@ -0,0 +1,32 @@ +# Pipe Chain Styles + + +## Pipe Start + +- raw value +- blocks are extracted to variables +- ecto's `from` is allowed + +## Piped function rewrites + +- add parens to function calls `|> fun |>` => `|> fun() |>` +- remove unnecessary `then/2`: `|> then(&f(&1, ...))` -> `|> f(...)` +- add `then` when defining anon funs in pipe `|> (& &1).() |>` => `|> |> then(& &1) |>` + +## Piped function optimizations + +- tries to fit everything on one line +- `lhs |> Enum.reverse() |> Enum.concat(enum)` => `lhs |> Enum.reverse(enum)` (also Kernel.++) +- `lhs |> Enum.filter(filterer) |> Enum.count()` => `lhs |> Enum.count(count)` +- `lhs |> Enum.map(mapper) |> Enum.join(joiner)` => `lhs |> Enum.map_join(joiner, mapper)` +- `lhs |> Enum.map(mapper) |> Enum.into(empty_map)` => `lhs |> Map.new(mapper)` +- `lhs |> Enum.map(mapper) |> Enum.into(collectable)` => `lhs |> Enum.into(collectable, mapper)` +- `lhs |> Enum.into(%{}, ...) => lhs |> Map.new(...)` +- `lhs |> Enum.map(mapper) |> Map.new()` => `lhs |> Map.new(mapper)` mapset & keyword also +- `lhs |> Map.merge(%{key: value}) => lhs |> Map.put(key, value)` (keyword literal also) + +## Unpiping Single Pipes + +- notably, optimizations might turn a 2 pipe into a single pipe +- doesn't unpipe when we're starting w/ quote +- pretty straight forward i daresay diff --git a/docs/single_node.cheatmd b/docs/single_node.cheatmd new file mode 100644 index 0000000..beb4bba --- /dev/null +++ b/docs/single_node.cheatmd @@ -0,0 +1,52 @@ +# Single Node Styles + +## Elixir Deprecation Rewrites + +1.15+ + +- Logger.warn -> Logger.warning +- Path.safe_relative_to/2 => Path.safe_relative/2 +- Enum/String.slice/2 w/ ranges -> explicit steps +- ~R/my_regex/ -> ~r/my_regex/ +- Date.range/2 -> Date.range/3 when decreasing range +- IO.read/bin_read -> use `:eof` instead of `:all` +1.16+ +- File.stream!(file, options, line_or_bytes) => File.stream!(file, line_or_bytes, options) + +## Literal Rewrites + +- string sigils +- large base 10 numbers + +## Function Optimizations + +These apply to the piped versions as well + +- Enum.into(%{}/Map/Keyword/MapSet.new) -> X.new +- Map/Keyword.merge w/ single key literal -> X.put +- Enum.reverse(foo) ++ bar -> Enum.reverse(foo, bar) + +## Function Readability + +- Timex.now/0 -> DateTime.utc_now/0 +- DateModule.compare(x, y) == :lt/:gt -> DateModule.before?/after? + +## Code Readability +- put matches on right +- `Credo.Check.Readability.PreferImplicitTry` + +## Consistency +- `def foo()` -> `def foo` + +## Credo Rules This Hit + +* Credo.Check.Consistency.ParameterPatternMatching +* Credo.Check.Readability.LargeNumbers +* Credo.Check.Readability.ParenthesesOnZeroArityDefs +* Credo.Check.Readability.PreferImplicitTry +* Credo.Check.Readability.StringSigils +* Credo.Check.Readability.WithSingleClause +* Credo.Check.Refactor.CaseTrivialMatches +* Credo.Check.Refactor.CondStatements +* Credo.Check.Refactor.RedundantWithClauseResult +* Credo.Check.Refactor.WithClauses diff --git a/lib/style/deprecations.ex b/lib/style/deprecations.ex index 22b922c..6805008 100644 --- a/lib/style/deprecations.ex +++ b/lib/style/deprecations.ex @@ -23,7 +23,6 @@ defmodule Styler.Style.Deprecations do do: {{:., dm, [{:__aliases__, am, [:Logger]}, :warning]}, funm, args} # Path.safe_relative_to/2 => Path.safe_relative/2 - # Path.safe_relative/2 is available since v1.14 # TODO: Remove after Elixir v1.19 defp style({{:., dm, [{_, _, [:Path]} = mod, :safe_relative_to]}, funm, args}), do: {{:., dm, [mod, :safe_relative]}, funm, args} diff --git a/lib/style/pipes.ex b/lib/style/pipes.ex index 1b2e269..8e31fc2 100644 --- a/lib/style/pipes.ex +++ b/lib/style/pipes.ex @@ -260,7 +260,7 @@ defmodule Styler.Style.Pipes do Style.set_line({:|>, [], [lhs, rhs]}, dm[:line]) end - # lhs |> Enum.into(%{}, ...) => lhs |> Map.new(...) + # `lhs |> Enum.into(%{}, ...)`` => `lhs |> Map.new(...)`` defp fix_pipe({:|>, meta, [lhs, {{:., dm, [{_, _, [:Enum]}, :into]}, _, [collectable | rest]}]} = node) do replacement = case collectable do @@ -277,7 +277,7 @@ defmodule Styler.Style.Pipes do if replacement, do: {:|>, meta, [lhs, {replacement, dm, rest}]}, else: node end - # `lhs |> Enum.map(mapper) |> Map.new()` => `lhs |> Map.new(mapper) + # `lhs |> Enum.map(mapper) |> Map.new()` => `lhs |> Map.new(mapper)`` defp fix_pipe( pipe_chain(lhs, {{:., _, [{_, _, [enum]}, :map]}, _, [mapper]}, {{:., _, [{_, _, [mod]}, :new]} = new, nm, []}) ) diff --git a/test/style/pipes_test.exs b/test/style/pipes_test.exs index 79ee89a..a523165 100644 --- a/test/style/pipes_test.exs +++ b/test/style/pipes_test.exs @@ -504,6 +504,9 @@ defmodule Styler.Style.PipesTest do assert_style "a |> then(&fun/1)", "fun(a)" assert_style "a |> then(&fun(&1)) |> c", "a |> fun() |> c()" assert_style "a |> then(&fun(&1, d)) |> c", "a |> fun(d) |> c()" + assert_style "a |> then(&M.f(&1)) |> c", "a |> M.f() |> c()" + + # Doens't rewrite multiple refs / non-starting argument assert_style "a |> then(&fun(d, &1)) |> c()" assert_style "a |> then(&fun(&1, d, %{foo: &1})) |> c()" @@ -546,6 +549,25 @@ defmodule Styler.Style.PipesTest do ) end + test "reverse/Kernel.++" do + assert_style("a |> Enum.reverse(bar) |> Kernel.++(foo)") + assert_style("a |> Enum.reverse() |> Kernel.++(foo)", "Enum.reverse(a, foo)") + + assert_style( + """ + a + |> Enum.reverse() + |> Kernel.++([bar, baz]) + |> Enum.sum() + """, + """ + a + |> Enum.reverse([bar, baz]) + |> Enum.sum() + """ + ) + end + test "filter/count" do for enum <- ~w(Enum Stream) do assert_style(