Skip to content

Commit

Permalink
refactor(Parser): set consistent opts
Browse files Browse the repository at this point in the history
Move trace task to source-controlled dir.
Update Trace
  • Loading branch information
ckoch-cars committed Aug 20, 2022
1 parent 0490486 commit 8cad8f2
Show file tree
Hide file tree
Showing 6 changed files with 308 additions and 350 deletions.
18 changes: 14 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Use at your peril, _for now._

### Example
```elixir
mix ex_factor --module TestModule.Here --function my_func --arity 1 --target NewModule.Test
mix ex_factor --module TestModule.Here --function my_func --arity 1 --target NewModule.There
```

## Roadmap TODONE
Expand All @@ -23,24 +23,24 @@ Use at your peril, _for now._
- [X] github actions, run test suite
- [X] Add Mix.Task tests
- [X] Add CLI tests
- [X] Add and configure CHANGELOG tracking.
- [X] Support opt-out of format-ing
- [X] Option to only change the module name throughout the project
- [X] update code to rely on compilation tracers, instead of XREF
- [X] With module-only option, ensure we remove changed aliases
- [] defdelegate

## Roadmap TODO

- [] find private functions references in refactored fn bodies.
- [] Add and configure CHANGELOG tracking.
- [] Add test for one file containing more than one `defmodule`
- [] Add test for nested defmodules.
- [] update test file refs by CLI option
- [] find dead functions
- [] find module attrs and also move them?
- [] find types referenced in the moved specs
- [] find private functions references in refactored fn bodies.
- [] git stage all changes?
- [] How does this work with macro code? Does that even make sense as a case to handle?
- [] Update .exs files too?
- [] Write tests to ensure we can find modules across umbrella apps.
- [] Add configuration hooks?
- [] ElixirLS integration for VSCode?
Expand Down Expand Up @@ -72,3 +72,13 @@ Alternate name:
## License

See [LICENSE](https://github.com/ckochx/ex_factor/blob/main/LICENSE)

## Miscellaneous resources

- https://dorgan.netlify.app/posts/2021/04/the_elixir_ast/
- https://www.educative.io/courses/metaprogramming-elixir/7DXEpKlj3Rr
- https://elixirforum.com/t/is-there-a-complete-elixir-ast-reference/38923/3
- https://www.botsquad.com/2019/04/11/the-ast-explained/
- https://elixirforum.com/t/getting-each-stage-of-elixirs-compilation-all-the-way-to-the-beam-bytecode/1873/8
- http://gomoripeti.github.io/beam_by_example/
- https://en.wikipedia.org/wiki/BEAM_(Erlang_virtual_machine)
12 changes: 7 additions & 5 deletions lib/ex_factor/parser.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,23 @@ defmodule ExFactor.Parser do
context of a Mix app and that every file contains one or more `defmodule` blocks.
"""

@code_options [:line, token_metadata: true, columns: true]

@doc """
Parse the contents of a filepath in an Abstract Syntaxt Tree (AST) and
extraxct the block contents of the module at the filepath.
"""
def read_file(filepath) when is_binary(filepath) do
contents = File.read!(filepath)
list = String.split(contents, "\n")
{:ok, ast} = Code.string_to_quoted(contents, token_metadata: true)
{:ok, ast} = Code.string_to_quoted(contents, @code_options)
{ast, list}
end

def block_contents(filepath) when is_binary(filepath) do
filepath
|> File.read!()
|> Code.string_to_quoted(token_metadata: true)
|> Code.string_to_quoted(@code_options)
|> block_contents()
end

Expand All @@ -34,7 +36,7 @@ defmodule ExFactor.Parser do
def all_functions(filepath) when is_binary(filepath) do
filepath
|> File.read!()
|> Code.string_to_quoted([:line, token_metadata: true, columns: true])
|> Code.string_to_quoted(@code_options)
|> all_functions()
end

Expand All @@ -51,7 +53,7 @@ defmodule ExFactor.Parser do
def public_functions(filepath) when is_binary(filepath) do
filepath
|> File.read!()
|> Code.string_to_quoted([:line, token_metadata: true, columns: true])
|> Code.string_to_quoted(@code_options)
|> public_functions()
end

Expand All @@ -67,7 +69,7 @@ defmodule ExFactor.Parser do
def private_functions(filepath) when is_binary(filepath) do
filepath
|> File.read!()
|> Code.string_to_quoted([:line, token_metadata: true, columns: true])
|> Code.string_to_quoted(@code_options)
|> private_functions()
end

Expand Down
1 change: 1 addition & 0 deletions mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ defmodule ExFactor.MixProject do

defp deps do
[
{:changex, ">= 0.0.0", only: :dev, runtime: false, optional: true, app: false},
{:ex_doc, ">= 0.0.0", only: :dev, runtime: false, optional: true, app: false}
]
end
Expand Down
1 change: 1 addition & 0 deletions mix.lock
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
%{
"changex": {:hex, :changex, "0.3.0", "ccba573ffc7f42b7535a31ee457c7e6d23628480d17afb0a7ce9695dfbde2ba1", [:mix], [], "hexpm", "8d2a4549f006835ef7d26bbf1e452d0d4c4093865db9998b50aa426cb2fe19b6"},
"earmark_parser": {:hex, :earmark_parser, "1.4.26", "f4291134583f373c7d8755566122908eb9662df4c4b63caa66a0eabe06569b0a", [:mix], [], "hexpm", "48d460899f8a0c52c5470676611c01f64f3337bad0b26ddab43648428d94aabc"},
"ex_doc": {:hex, :ex_doc, "0.28.4", "001a0ea6beac2f810f1abc3dbf4b123e9593eaa5f00dd13ded024eae7c523298", [:mix], [{:earmark_parser, "~> 1.4.19", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "bf85d003dd34911d89c8ddb8bda1a958af3471a274a4c2150a9c01c78ac3f8ed"},
"makeup": {:hex, :makeup, "1.1.0", "6b67c8bc2882a6b6a445859952a602afc1a41c2e08379ca057c0f525366fc3ca", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "0a45ed501f4a8897f580eabf99a2e5234ea3e75a4373c8a52824f6e873be57a6"},
Expand Down
57 changes: 57 additions & 0 deletions support/trace_task.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
defmodule Mix.Tasks.ExFactorRunner do
@moduledoc """
In order to run ExFactor on ExFactor, we need to make some files available "outside of" ExFactor, or more precisely along side of ExFactor.
We need to have a process running during compilation that can run the `ExFactor.Server` Agent.
Additionally we need the setup from `ExFactor.Traces`, we could replicate this here, but why
bother copying something that we'll reuse.
"""
use Mix.Task

@impl true
def run(args) do
unless Version.match?(System.version(), ">= 1.13.0") do
Mix.raise("Elixir v1.13+ is required!")
end

Code.compiler_options(debug_info: true, parser_options: [columns: true, token_metadata: true])

Code.compile_file("./lib/ex_factor/server.ex", File.cwd!())
:timer.sleep(20)

Code.compile_file("./lib/ex_factor/traces.ex", File.cwd!())
:timer.sleep(20)

case args do
[] ->

do_run()

_ ->
Mix.raise("Additional CLI args are not supported.\n\n\tUsage: elixir -r support/trace_task.ex -S mix ex_factor_runner")
end
end

defp do_run() do
ExFactor.Traces.setup()

Mix.Task.rerun("compile", ["--force", "--tracer=ExFactor.Tracer"]) |> IO.inspect(label: "")

entries = ExFactor.Server.entries()
entries_string = inspect(entries, limit: :infinity, printable_limit: :infinity)
File.write!("./tmp/trace_entries", entries_string)

file_contents = [
"defmodule ExFactor.Support.Trace do",
"def trace_function do",
entries_string,
"end",
"end"
]
|> Enum.join("\n")

path = "./test/support/trace.ex"
File.write!(path, file_contents)
Mix.Tasks.Format.run([path])
end
end
Loading

0 comments on commit 8cad8f2

Please sign in to comment.