Search documentation of Ragex

# Ragex.Editor.Refactor.Elixir (Ragex v0.4.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

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

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

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

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

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

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

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

Extracts multiple functions from a module into a new module.

find_function_calls(content, function_name, arity \\ nil)

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

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

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

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

Adds, removes, or updates module attributes.

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

Moves a function from one module to another.

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

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

rename_module(content, old_name, new_name)

Renames a module and all references to it.

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

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