# `ExAST.Patcher`
[🔗](https://github.com/elixir-vibe/ex_ast/blob/v0.11.0/lib/ex_ast/patcher.ex#L1)

Finds and replaces AST patterns in source code.

Accepts source strings, AST nodes, or Sourceror zippers as input.
Patterns and replacements can be strings or quoted expressions.

Source-string input preserves formatting via `Sourceror.patch_string/2`.
AST/zipper input returns modified AST trees.

    # All equivalent
    Patcher.find_all(source, "IO.inspect(_)")
    Patcher.find_all(ast, quote(do: IO.inspect(_)))
    Patcher.find_all(zipper, quote(do: IO.inspect(_)))

    import ExAST.Selector

    Patcher.find_all(source, pattern("defmodule _ do ... end") |> descendant("IO.inspect(_)"))

# `match`

```elixir
@type match() :: %{
  node: Macro.t(),
  range: Sourceror.Range.t() | nil,
  captures: ExAST.Pattern.captures(),
  source: String.t() | nil
}
```

# `named_pattern`

```elixir
@type named_pattern() ::
  {pattern_name(), ExAST.Pattern.pattern() | ExAST.Selector.t()}
```

# `pattern_name`

```elixir
@type pattern_name() :: term()
```

# `tagged_match`

```elixir
@type tagged_match() :: %{:pattern =&gt; pattern_name(), optional(atom()) =&gt; term()}
```

# `find_all`

```elixir
@spec find_all(
  String.t() | Sourceror.Zipper.t() | Macro.t(),
  ExAST.Pattern.pattern() | ExAST.Selector.t(),
  keyword()
) :: [match()]
```

Finds all occurrences of `pattern`.

The first argument can be a source string, a `Sourceror.Zipper`, or a raw AST.
The pattern can be a string or a quoted expression.

Returns a list of match maps with:

  * `:node` — the matched AST node
  * `:range` — a `Sourceror.Range.t()` with line/column positions, or `nil`
  * `:captures` — a map of captured names to AST nodes
  * `:source` — the matched source text, or `nil` for AST/zipper input

Range fields are accessed as keyword lists:

    match.range.start[:line]   #=> line number (1-based)
    match.range.start[:column] #=> column number (1-based)
    match.range.end[:line]
    match.range.end[:column]

## Options

  * `:inside` — only match nodes nested within an ancestor matching this pattern
  * `:not_inside` — reject nodes nested within an ancestor matching this pattern

# `find_many`

```elixir
@spec find_many(
  String.t() | Sourceror.Zipper.t() | Macro.t(),
  [named_pattern()]
  | %{required(pattern_name()) =&gt; ExAST.Pattern.pattern() | ExAST.Selector.t()},
  keyword()
) :: [tagged_match()]
```

Finds matches for multiple named patterns in a single pass where possible.

`patterns` may be a keyword list or a map. Returned matches include a
`:pattern` field with the matching pattern name:

    Patcher.find_many(source,
      inspect_call: "IO.inspect(expr)",
      debug_call: "dbg(expr)"
    )

This is useful for analyzers that run many independent pattern checks over
the same source tree. Single-node patterns are compiled once and scanned
together; selectors and multi-node sequence patterns fall back to the regular
matcher while keeping the same tagged result shape.

# `replace_all`

```elixir
@spec replace_all(
  String.t(),
  ExAST.Pattern.pattern() | ExAST.Selector.t(),
  ExAST.Pattern.pattern(),
  keyword()
) :: String.t()
@spec replace_all(
  Sourceror.Zipper.t() | Macro.t(),
  ExAST.Pattern.pattern() | ExAST.Selector.t(),
  ExAST.Pattern.pattern(),
  keyword()
) :: Macro.t()
```

Replaces all occurrences of `pattern` with `replacement`.

When given a source string, returns a modified source string with
formatting preserved. When given a zipper or AST, returns modified AST.

Pattern and replacement can be strings or quoted expressions.
Captures from the pattern are substituted into the replacement template.
Accepts the same `:inside` / `:not_inside` options as `find_all/3`.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
