Ragex.Editor.Refactor.Elixir (Ragex v0.12.0)

View Source

Elixir-specific AST manipulation for semantic refactoring.

Provides functions to rename functions and modules by parsing and transforming Elixir AST, preserving comments and formatting where possible.

Summary

Functions

Changes a function signature by adding, removing, reordering, or renaming parameters.

Converts function visibility between public (def) and private (defp).

Extracts multiple functions from a module into a new module.

Finds all function calls to a specific function in the AST.

Inlines a function by replacing all its calls with the function body.

Adds, removes, or updates module attributes.

Renames a function definition and all its calls within a source file.

Renames a module and all references to it.

Renames a function parameter and all its references within the function body.

Functions

change_signature(content, module_name, function_name, old_arity, signature_changes, opts \\ [])

@spec change_signature(
  String.t(),
  atom(),
  atom(),
  non_neg_integer(),
  map(),
  keyword()
) :: {:ok, String.t()} | {:error, term()}

Changes a function signature by adding, removing, reordering, or renaming parameters.

Parameters

  • content: Source code as string
  • module_name: Module containing the function
  • function_name: Function to modify
  • old_arity: Current function arity
  • signature_changes: Map describing the changes
  • opts: Options (currently unused)

Signature Changes Format

The signature_changes map can contain:

  • :add_params - List of params to add: [%{name: atom, position: integer, default: any}]
  • :remove_params - List of param positions to remove (0-indexed): [0, 2]
  • :reorder_params - New param order (0-indexed positions): [2, 0, 1]
  • :rename_params - List of renames: [{old_name, new_name}]

Returns

  • {:ok, new_content} on success
  • {:error, reason} on failure

Examples

# Add a parameter with default value
changes = %{add_params: [%{name: :opts, position: 2, default: []}]}
change_signature(content, :MyModule, :process, 2, changes)

# Remove second parameter (position 1)
changes = %{remove_params: [1]}
change_signature(content, :MyModule, :calculate, 3, changes)

# Reorder parameters: swap first and second
changes = %{reorder_params: [1, 0, 2]}
change_signature(content, :MyModule, :transform, 3, changes)

# Rename parameters
changes = %{rename_params: [{:x, :input}, {:y, :output}]}
change_signature(content, :MyModule, :convert, 2, changes)

# Combine multiple operations
changes = %{
  add_params: [%{name: :config, position: 0, default: %{}}],
  remove_params: [2],
  rename_params: [{:data, :payload}]
}
change_signature(content, :MyModule, :handler, 3, changes)

convert_visibility(content, module_name, function_name, arity, visibility, opts \\ [])

@spec convert_visibility(
  String.t(),
  atom(),
  atom(),
  non_neg_integer(),
  :public | :private,
  keyword()
) :: {:ok, String.t()} | {:error, term()}

Converts function visibility between public (def) and private (defp).

Parameters

  • content: Source code as string
  • module_name: Module containing the function
  • function_name: Function to modify
  • arity: Function arity
  • visibility: :public or :private
  • opts: Options
    • :add_doc - Add documentation when making public (default: false)

Returns

  • {:ok, new_content} on success
  • {:error, reason} on failure

extract_function(content, module_name, source_function, source_arity, new_function_name, arg, opts \\ [])

@spec extract_function(
  String.t(),
  atom(),
  atom(),
  non_neg_integer(),
  atom(),
  {pos_integer(), pos_integer()},
  keyword()
) :: {:ok, String.t()} | {:error, term()}

Extracts a range of lines from a function into a new function.

Parameters

  • content: Source code as string
  • module_name: Module containing the function
  • source_function: Function to extract from
  • source_arity: Arity of source function
  • new_function_name: Name for the extracted function
  • line_range: {start_line, end_line} tuple (1-indexed)
  • opts: Options
    • :placement - :after_source | :before_source | :end_of_module (default: :after_source)

    • :visibility - :public | :private (default: :private)

    • :add_doc - boolean (default: false)

Returns

  • {:ok, new_content} on success
  • {:error, reason} on failure

extract_module(source_content, source_module, new_module, functions, opts \\ [])

@spec extract_module(
  String.t(),
  atom(),
  atom(),
  [{atom(), non_neg_integer()}],
  keyword()
) :: {:ok, %{source: String.t(), target: String.t()}} | {:error, term()}

Extracts multiple functions from a module into a new module.

Parameters

  • source_content: Source module code
  • source_module: Source module name
  • new_module: New module name
  • functions: List of {function_name, arity} tuples to extract
  • opts: Options
    • :add_moduledoc - boolean (default: true)
    • :update_aliases - boolean (default: true)

Returns

  • {:ok, %{source: new_source, target: new_module_content}} on success
  • {:error, reason} on failure

Examples

functions = [{:helper, 1}, {:process, 2}]
extract_module(content, :MyModule, :MyModule.Helpers, functions)

find_function_calls(content, function_name, arity \\ nil)

@spec find_function_calls(String.t(), atom() | String.t(), non_neg_integer() | nil) ::
  {:ok, [non_neg_integer()]} | {:error, term()}

Finds all function calls to a specific function in the AST.

Returns a list of line numbers where the function is called.

inline_function(content, module_name, function_name, arity, opts \\ [])

@spec inline_function(
  String.t(),
  atom(),
  atom(),
  non_neg_integer(),
  keyword()
) :: {:ok, String.t()} | {:error, term()}

Inlines a function by replacing all its calls with the function body.

Parameters

  • content: Source code as string
  • module_name: Module containing the function
  • function_name: Function to inline
  • arity: Function arity
  • opts: Options
    • :remove_definition - Remove function definition after inlining (default: true)

Returns

  • {:ok, new_content} on success
  • {:error, reason} on failure

Examples

iex> content = """
...> defmodule Test do
...>   defp helper(x), do: x * 2
...>   def main(a), do: helper(a) + 1
...> end
...> """
iex> Elixir.inline_function(content, :Test, :helper, 1)
{:ok, "defmodule Test do

def main(a), do: a * 2 + 1 end"}

modify_attributes(content, module_name, changes, opts \\ [])

@spec modify_attributes(String.t(), atom(), list(), keyword()) ::
  {:ok, String.t()} | {:error, term()}

Adds, removes, or updates module attributes.

Parameters

  • content: Source code as string
  • module_name: Module to modify (currently unused, modifies all modules)
  • changes: List of change tuples:
    • {:add, attribute_name, value} - Add new attribute
    • {:remove, attribute_name} - Remove attribute
    • {:update, attribute_name, new_value} - Update existing attribute
  • opts: Options (currently unused)

Returns

  • {:ok, new_content} on success
  • {:error, reason} on failure

Examples

iex> changes = [
...>   {:add, :vsn, "1.0.0"},
...>   {:remove, :deprecated},
...>   {:update, :moduledoc, "Updated documentation"}
...> ]
iex> modify_attributes(content, :MyModule, changes)
{:ok, updated_content}

move_function(source_content, target_content, source_module, target_module, function_name, arity, opts \\ [])

@spec move_function(
  String.t(),
  String.t() | nil,
  atom(),
  atom(),
  atom(),
  non_neg_integer(),
  keyword()
) :: {:ok, %{source: String.t(), target: String.t()}} | {:error, term()}

Moves a function from one module to another.

Parameters

  • source_content: Source module code
  • target_content: Target module code (or nil for new module)
  • source_module: Source module name
  • target_module: Target module name
  • function_name: Function to move
  • arity: Function arity
  • opts: Options
    • :placement - :start | :end (default: :end)

    • :update_references - boolean (default: true)

Returns

  • {:ok, %{source: new_source, target: new_target}} on success
  • {:error, reason} on failure

rename_function(content, old_name, new_name, arity \\ nil)

@spec rename_function(
  String.t(),
  atom() | String.t(),
  atom() | String.t(),
  non_neg_integer() | nil
) :: {:ok, String.t()} | {:error, term()}

Renames a function definition and all its calls within a source file.

Parameters

  • content: Source code as string
  • old_name: Current function name (atom or string)
  • new_name: New function name (atom or string)
  • arity: Function arity (nil to rename all arities)

Returns

  • {:ok, new_content} on success
  • {:error, reason} on failure

Examples

iex> content = "def old_func(x), do: x + 1"
iex> Elixir.rename_function(content, :old_func, :new_func, 1)
{:ok, "def new_func(x), do: x + 1"}

rename_module(content, old_name, new_name)

@spec rename_module(String.t(), atom() | String.t(), atom() | String.t()) ::
  {:ok, String.t()} | {:error, term()}

Renames a module and all references to it.

Parameters

  • content: Source code as string
  • old_name: Current module name (atom or string)
  • new_name: New module name (atom or string)

Returns

  • {:ok, new_content} on success
  • {:error, reason} on failure

rename_parameter(content, module_name, function_name, arity, old_param_name, new_param_name, opts \\ [])

@spec rename_parameter(
  String.t(),
  atom(),
  atom(),
  non_neg_integer(),
  atom(),
  atom(),
  keyword()
) :: {:ok, String.t()} | {:error, term()}

Renames a function parameter and all its references within the function body.

Parameters

  • content: Source code as string
  • module_name: Module containing the function
  • function_name: Function to modify
  • arity: Function arity
  • old_param_name: Current parameter name
  • new_param_name: New parameter name
  • opts: Options (currently unused)

Returns

  • {:ok, new_content} on success
  • {:error, reason} on failure