Installation

Add ExAST as a dev dependency:

def deps do
  [{:ex_ast, "~> 0.11", only: [:dev, :test], runtime: false}]
end

Find every IO.inspect call in your project:

mix ex_ast.search 'IO.inspect(_)' lib/

Patterns are plain Elixir syntax — _ is a wildcard, variables capture:

# Any arity
mix ex_ast.search 'IO.inspect(...)' lib/

# Capture the argument
mix ex_ast.search 'IO.inspect(expr)' lib/

# Specific two-arg calls
mix ex_ast.search 'IO.inspect(_, _)' lib/

First replace

Remove all dbg calls — the captured expr substitutes into the replacement:

mix ex_ast.replace 'dbg(expr)' 'expr' lib/

Preview without writing files:

mix ex_ast.replace --dry-run 'use Mix.Config' 'import Config' lib/

Programmatic API

Everything the CLI does is available as functions:

# Search files
ExAST.search("lib/", "IO.inspect(_)")
#=> [%{file: "lib/worker.ex", line: 12, source: "IO.inspect(data)", captures: %{}}]

# Replace in files
ExAST.replace("lib/", "dbg(expr)", "expr")
#=> [{"lib/worker.ex", 2}]

# Low-level: work with source strings
ExAST.Patcher.find_all(source_code, "IO.inspect(_)")
#=> [%{node: ..., range: ..., captures: %{}, source: "IO.inspect(data)"}]

ExAST.Patcher.replace_all(source_code, "dbg(expr)", "expr")
#=> "source with dbg calls removed"

What's next