Recode
A linter with autocorrection and a refactoring tool.
recode is an experimental project to play around with the great
sourceror package by @doorgan.
This library is still under development, breaking changes are expected.
The same is true for sourceror and most of recode‘s functionality is based
on sourceror.
For now, recode corrects only a few things:
AliasExpansionexpands multi aliasesAliasOrderorders aliases alphabeticallyFormatruns the Elixir formatterPipeFunOneadds()to one arity functions in pipesSinglePipecorrects single pipesTestFileExtrenames test file extensions to*.exs
It is also possible to run recode in a none-autocorect mode to just lint your
code.
There is also one mix task for code refactoring. The task mix recode.rename
renames a function and all their function calls.
Installation
The package can be installed by adding recode to your list of dependencies
in mix.exs:
def deps do
[
{:recode, "~> 0.1"}
]
endDocumentation can be found at https://hexdocs.pm/recode.
Usage
To start with Foo a configuration file is needed.
mix recode.gen.config
This mix task generates the config file .recode.exs.
alias Recode.Task
[
# Can also be set/reset with "--autocorrect"/"--no-autocorrect".
autocorrect: true,
# With "--dry" no changes will be written to the files.
# Can also be set/reset with "--dry"/"--no-dry".
# If dry is true then verbose is also active.
dry: false,
# Can also be set/reset with "--verbose"/"--no-verbose".
verbose: false,
inputs: ["{config,lib,test}/**/*.{ex,exs}"],
formatter: {Recode.Formatter, []},
tasks: [
{Task.AliasExpansion, []},
{Task.AliasOrder, []},
{Task.PipeFunOne, []},
{Task.SinglePipe, []},
{Task.Specs, [only: :visible, exclude: "test/**/*.{ex,exs}"]},
{Task.TestFileExt, []}
]
]mix recode
This mix tasks runs the linter with autocorrection. The switch --dry prevents
the update of the files and shows all changes in the console.
> cd examples/my_code
> mix recode --dry
Found 11 files, including 2 scripts.
.............................................................................
File: lib/my_code.ex
[Specs 15/3] Functions should have a @spec type specification.
File: lib/my_code/alias_expansion.ex
Updates: 1
Changed by: AliasExpansion
001 |defmodule MyCode.AliasExpansion do
002 - | alias MyCode.{PipeFunOne, SinglePipe}
002 + | alias MyCode.PipeFunOne
003 + | alias MyCode.SinglePipe
004 |
005 | def foo(x) do
[Specs 5/3] Functions should have a @spec type specification.
File: lib/my_code/alias_order.ex
Updates: 2
Changed by: AliasOrder, AliasExpansion
012 |
013 |defmodule Mycode.AliasOrder do
014 - | alias MyCode.SinglePipe
014 + | alias MyCode.Echo
015 + | alias MyCode.Foxtrot
016 | alias MyCode.PipeFunOne
017 - | alias MyCode.{Foxtrot, Echo}
017 + | alias MyCode.SinglePipe
018 |
019 | @doc false
File: lib/my_code/fun.ex
Updates: 1
Changed by: Format
002 | @moduledoc false
003 |
004 - |
005 - |
006 - |
007 - |
008 - |
004 | def noop(x), do: x
005 |end
File: lib/my_code/multi.ex
Updates: 2
Changed by: SinglePipe, PipeFunOne
007 |
008 | def pipe(x) do
009 - | x |> double |> double()
009 + | x |> double() |> double()
010 | end
011 |
012 | def single(x) do
013 - | x |> double()
013 + | double(x)
014 | end
015 |
File: lib/my_code/pipe_fun_one.ex
Updates: 1
Changed by: PipeFunOne
005 |
006 | def pipe(x) do
007 - | x |> double |> double()
007 + | x |> double() |> double()
008 | end
009 |end
File: lib/my_code/singel_pipe.ex
Updates: 1
Changed by: SinglePipe
005 |
006 | def single_pipe(x) do
007 - | x |> double()
007 + | double(x)
008 | end
009 |
010 - | def reverse(a), do: a |> Enum.reverse()
010 + | def reverse(a), do: Enum.reverse(a)
011 |end
012 |
File: test/my_code_test.exs
Updates: 1
Changed by: TestFileExt
Moved from: test/my_code_test.ex
The switch --no-autocorrect runs the linter without any file changes.
> cd examples/my_code
> mix recode --no-autocorrect
Found 11 files, including 2 scripts.
.............................................................................
File: lib/my_code.ex
[Specs 15/3] Functions should have a @spec type specification.
File: lib/my_code/alias_expansion.ex
[AliasExpansion 2/3] Avoid multi aliases.
[Specs 4/3] Functions should have a @spec type specification.
File: lib/my_code/alias_order.ex
[AliasOrder 15/3] The alias `MyCode.PipeFunOne` is not alphabetically ordered among its group
[AliasOrder 16/3] The alias `MyCode` is not alphabetically ordered among its group
[AliasOrder 16/26] The alias `Echo` is not alphabetically ordered among its multi group
[AliasExpansion 16/3] Avoid multi aliases.
File: lib/my_code/fun.ex
[Format -/-] The file is not formatted.
File: lib/my_code/multi.ex
[PipeFunOne 9/7] Use parentheses for one-arity functions in pipes.
[SinglePipe 13/7] Use a function call when a pipeline is only one function long.
File: lib/my_code/pipe_fun_one.ex
[PipeFunOne 7/7] Use parentheses for one-arity functions in pipes.
File: lib/my_code/singel_pipe.ex
[SinglePipe 7/7] Use a function call when a pipeline is only one function long.
[SinglePipe 10/25] Use a function call when a pipeline is only one function long.
File: test/my_code_test.ex
[TestFileExt -/-] The file must be renamed to test/my_code_test.exs so that ExUnit can find it.mix recode.rename
A mix task to rename a function and all their function calls.
> cd examples/my_code
> mix recode.rename --dry MyCode.SinglePipe.double dbl
Found 11 files, including 2 scripts.
...........
File: lib/my_code/alias_expansion.ex
Updates: 1
Changed by: Rename
003 |
004 | def foo(x) do
005 - | SinglePipe.double(x) + PipeFunOne.double(x)
005 + | SinglePipe.dbl(x) + PipeFunOne.double(x)
006 | end
007 |end
File: lib/my_code/alias_order.ex
Updates: 1
Changed by: Rename
018 | @doc false
019 | def foo do
020 - | {SinglePipe.double(2), PipeFunOne.double(3)}
020 + | {SinglePipe.dbl(2), PipeFunOne.double(3)}
021 | end
022 |
File: lib/my_code/singel_pipe.ex
Updates: 1
Changed by: Rename
002 | @moduledoc false
003 |
004 - | def double(x), do: x + x
004 + | def dbl(x), do: x + x
005 |
006 | def single_pipe(x) do
007 - | x |> double()
007 + | x |> dbl()
008 | end
009 |Refactored code should be compellable, but it is not guaranteed.
Differences to Credo
recode was started as a plugin for credo. Unfortunately it was not possible
to implement autocorrection as a plugin because the traversation of the code does
not support changing the code.
Maybe some code lines from recode could be used as inspiration for credo
to bring the autocorrect feature to credo.
Other differences:
recoderequiers Elixir 1.12,credorequiers Elixir 1.7recodehas autocorrectioncredohas much more checkerscredois fastercredohas more features