Rfx provides a catalog of automated refactoring operations for Elixir source code.
Rfx operations are meant to be embedded into editors, tools and end-user apps. See the experimental rfxi for an example app. Rfx depends on the excellent Sourceror to manipulate AST code and comments.
To get started with this pre-release code, clone the repo, then run > mix test
. NOTE this code is under heavy development - expect rapid changes
and bugs!
See Rfx.Catalog.OpsCat
for a live catalog of Operations.
Module Operations:
- rename module (
Rfx.Ops.Module.RenameModule
) - rename module attribute
- extract function
- inline function
Function Operations:
- rename function
- rename function parameter
- rename variable
- extract variable
- inline variable
Filesystem Operations:
- move directory (
Rfx.Ops.Filesys.MvDir
) - move file (
Rfx.Ops.Filesys.MvFile
)
Credo Operations:
- multi-alias (
Rfx.Ops.Credo.MultiAlias
)
Surface Operations:
- rename component
- rename property
- rename named-slot
PhxGen Operations:
- add route
- add controller
Project Operations:
- add dependency
- increment version
Prototype Operations:
- comment add (
Rfx.Ops.Proto.CommentAdd
) - comment del (
Rfx.Ops.Proto.CommentDel
) - no-op (
Rfx.Ops.Proto.NoOp
)
Each operation returns a change set (Rfx.Change.Set
) with a list of of
change requests (Rfx.Change.Request
). The change set is a data structure
that describes all the refactoring changes to be made for an operation.
A change request struct has elements for text edits and file actions (create, rename, delete).
Rfx provides an extensible catalog of converter functions:
Rfx.Change.Set.convert(changeset, :to_string) #> Returns the modified source code
Rfx.Change.Set.convert(changeset, :to_patchfile) #> Returns a unix-standard patchfile
Rfx.Change.Set.convert(changeset, :to_lsp) #> Returns a data structure for LSP
Rfx.Change.Set.convert(changeset, :to_pr) #> Returns a pull-request data structure
Rfx also provides a function that applies the change requests to the filesystem.
Rfx.Change.Set.apply!(changeset) #> Applies the changereqs to the filesystem
Each operation is coded in a standalone module that implements the Rfx.Ops
behavior. A refactoring operation may be applied to different scopes:
- Scope1: a code string
- Scope2: a single file
- Scope3: an entire project
- Scope4: an umbrella sub-application
- Scope5: a temp file
Each scope will generate a different set of change requests. Consider for
example the operation Rfx.Ops.Module.RenameModule
.
Rename_Module | # of Change Requests | Text Edits | File Actions |
---|---|---|---|
Scope1 code |
1 | Edit src & docs | NA |
Scope2 file |
1 | Edit src & docs | Rename Src file |
Scope3 project |
1 for each related project file | Edit src & docs | Rename Src & Test file |
Scope4 subapp |
1 for each related subapp file | Edit src & docs | Rename Src & Test file |
Scope5 tmpfile |
1 | Edit src & docs | NA |
Here's a code example:
#!/usr/bin/env elixir
Mix.install([ {:rfx, github: "andyl/rfx"} ])
"x = 1"
|> Rfx.Ops.Proto.CommentAdd.cl_code()
|> Rfx.Change.Set.convert(:to_string)
|> IO.inspect()
# [
# %Rfx.Change.Request{
# file_req: nil,
# text_req: %{diff: "0a1\n> # TestComment\n", input_text: "x = 1"},
# log: %{convert: %{to_string: "# TestComment\nx = 1"}}
# }
# ]
Rfx Operations are intended for use in developer tools:
- Elixir Scripts and Livebooks (examples in
rfx/scripts
andrfx/notebooks
) - CLI (see the experimental rfxi)
- Editor Plugins (see the experimental rfx_nvim)
- Generators and Mix tasks
- ElixirLs
- Credo
def deps do
[
{:rfx, github: "andyl/rfx"}
]
end