Igniter.Code.Pattern (igniter v0.8.0)

View Source

Pattern-based AST navigation and rewriting powered by ExAST.

Requires {:ex_ast, "~> 0.5"} (included as a dependency of Igniter).

Pattern syntax

Patterns are valid Elixir expressions:

SyntaxMeaning
_ or _nameWildcard — matches any node, not captured
name, exprCapture — matches any node, bound by name
...Ellipsis — matches zero or more nodes
Everything elseLiteral — must match exactly

Structs and maps match partially, pipes are normalized.

Examples

# Move to a node matching a pattern
{:ok, zipper} = Pattern.move_to(zipper, "Repo.get!(_, _)")

# Ellipsis — match any arity
{:ok, zipper} = Pattern.move_to(zipper, "Logger.info(...)")

# Check if current node matches
Pattern.matches?(zipper, "use GenServer")

# Find all matching nodes
zippers = Pattern.find_all(zipper, "Repo.get!(...)")

# Replace with captures
{:ok, zipper} = Pattern.replace(zipper,
  "Enum.map(list, f)", "Enum.flat_map(list, f)")

# Replace all occurrences
{:ok, zipper} = Pattern.replace_all(zipper,
  "Logger.debug(msg, ...)", "Logger.warning(msg, ...)")

# Context filters
{:ok, zipper} = Pattern.move_to(zipper,
  "Repo.get!(...)", not_inside: "test _ do _ end")

# Inside update_elixir_file
Igniter.update_elixir_file(igniter, path, fn zipper ->
  Pattern.replace_all(zipper,
    "Enum.map(list, f)", "Enum.flat_map(list, f)")
end)

# Project-level: replace across all matching files
Pattern.replace_in_all_files(igniter,
  "Logger.debug(msg, ...)", "Logger.warning(msg, ...)")

# ~p sigil for compile-time pattern parsing
import Igniter.Code.Pattern
Pattern.find_all(zipper, ~p"Repo.get!(...)")

Summary

Functions

Returns a list of zippers, one for each node matching pattern.

Returns true if the current node matches the pattern.

Moves to the first node matching pattern.

Replaces the first node matching pattern with replacement.

Replaces all nodes matching pattern with replacement.

Replaces all nodes matching pattern with replacement across all Elixir files in the project.

Replaces all nodes matching pattern with replacement in a single file.

Parses a pattern string into AST at compile time.

Types

pattern()

@type pattern() :: String.t() | Macro.t()

Functions

find_all(zipper, pattern, opts \\ [])

@spec find_all(Sourceror.Zipper.t(), pattern(), keyword()) :: [Sourceror.Zipper.t()]

Returns a list of zippers, one for each node matching pattern.

Options

  • :inside — only match inside ancestors matching this pattern
  • :not_inside — skip matches inside ancestors matching this pattern

matches?(zipper, pattern)

@spec matches?(Sourceror.Zipper.t(), pattern()) :: boolean()

Returns true if the current node matches the pattern.

Useful as a predicate inside Igniter.Code.Common.move_to/2, find_all_matching_modules, or similar callbacks.

Examples

Igniter.Code.Common.move_to(zipper, fn zipper ->
  Pattern.matches?(zipper, "use GenServer")
end)

move_to(zipper, pattern, opts \\ [])

@spec move_to(Sourceror.Zipper.t(), pattern(), keyword()) ::
  {:ok, Sourceror.Zipper.t()} | :error

Moves to the first node matching pattern.

Returns {:ok, zipper} positioned at the matched node, or :error.

Options

  • :inside — only match inside ancestors matching this pattern
  • :not_inside — skip matches inside ancestors matching this pattern

replace(zipper, pattern, replacement, opts \\ [])

@spec replace(Sourceror.Zipper.t(), pattern(), pattern(), keyword()) ::
  {:ok, Sourceror.Zipper.t()} | :error

Replaces the first node matching pattern with replacement.

Returns {:ok, zipper} with the modified tree, or :error if no match is found.

Options

  • :inside — only match inside ancestors matching this pattern
  • :not_inside — skip matches inside ancestors matching this pattern

replace_all(zipper, pattern, replacement, opts \\ [])

@spec replace_all(Sourceror.Zipper.t(), pattern(), pattern(), keyword()) ::
  {:ok, Sourceror.Zipper.t()} | :error

Replaces all nodes matching pattern with replacement.

Returns {:ok, zipper} with the modified tree. If no matches are found, returns the zipper unchanged.

Options

  • :inside — only match inside ancestors matching this pattern
  • :not_inside — skip matches inside ancestors matching this pattern

replace_in_all_files(igniter, pattern, replacement, opts \\ [])

@spec replace_in_all_files(Igniter.t(), pattern(), pattern(), keyword()) ::
  Igniter.t()

Replaces all nodes matching pattern with replacement across all Elixir files in the project.

Options

  • :inside — only match inside ancestors matching this pattern
  • :not_inside — skip matches inside ancestors matching this pattern

replace_in_file(igniter, path, pattern, replacement, opts \\ [])

@spec replace_in_file(Igniter.t(), String.t(), pattern(), pattern(), keyword()) ::
  Igniter.t()

Replaces all nodes matching pattern with replacement in a single file.

Wraps Igniter.update_elixir_file/3 with pattern-based replacement.

Options

  • :inside — only match inside ancestors matching this pattern
  • :not_inside — skip matches inside ancestors matching this pattern

sigil_p(arg, modifiers)

(macro)

Parses a pattern string into AST at compile time.

Avoids runtime string parsing. The result can be passed to any function in this module.

Examples

import Igniter.Code.Pattern

Pattern.find_all(zipper, ~p"IO.inspect(...)")
Pattern.replace_all(zipper, ~p"dbg(expr)", ~p"expr")