# `Minga.Config.Advice`
[🔗](https://github.com/jsmestad/minga/blob/main/lib/minga/config/advice.ex#L1)

Before/after/around/override advice for editor commands.

Advice functions wrap existing command execution, similar to Emacs's
`advice-add` system. Four phases are supported:

| Phase | Signature | Behavior |
|-------|-----------|----------|
| `:before` | `(state -> state)` | Transforms state before the command runs |
| `:after` | `(state -> state)` | Transforms state after the command runs |
| `:around` | `((state -> state), state -> state)` | Receives the original execute function; full control over whether/how it runs |
| `:override` | `(state -> state)` | Completely replaces the command; original never runs |

Uses ETS with `read_concurrency: true` for zero-contention reads in the
hot `dispatch_command` path. Writes only happen at config load/reload.

## Composition

Multiple advice functions for the same phase and command run in
registration order. For `:around`, they nest: the outermost advice
wraps the next one, which wraps the next, with the original command
at the center.

If an `:override` is registered, it replaces the command entirely.
Multiple overrides chain (last registered wins as the innermost).
`:before` and `:after` still run around an overridden command.

## Examples

    # Transform state before save
    Minga.Config.Advice.register(:before, :save, fn state ->
      state
    end)

    # Full control: conditionally skip formatting
    Minga.Config.Advice.register(:around, :format_buffer, fn execute, state ->
      if some_condition?(state) do
        execute.(state)
      else
        # Return state unchanged to skip the command
        state
      end
    end)

    # Completely replace a command
    Minga.Config.Advice.register(:override, :save, fn state ->
      my_custom_save(state)
    end)

# `around_fun`

```elixir
@type around_fun() :: ((map() -&gt; map()), map() -&gt; map())
```

Around advice: receives the execute function and state.

# `phase`

```elixir
@type phase() :: :before | :after | :around | :override
```

Advice phase.

# `state_fun`

```elixir
@type state_fun() :: (map() -&gt; map())
```

Before/after/override advice: transforms editor state.

# `advised?`

```elixir
@spec advised?(atom()) :: boolean()
```

Returns true if any advice of any phase is registered for the command.

# `advised?`

```elixir
@spec advised?(atom(), atom()) :: boolean()
```

# `disabled?`

```elixir
@spec disabled?(atom(), phase(), atom(), function()) :: boolean()
```

Returns true if a specific advice function has been disabled by the circuit breaker.

# `has_advice?`

```elixir
@spec has_advice?(phase(), atom()) :: boolean()
```

Returns true if any advice is registered for the given phase and command.

# `has_advice?`

```elixir
@spec has_advice?(atom(), phase(), atom()) :: boolean()
```

# `register`

```elixir
@spec register(phase(), atom(), function()) :: :ok | {:error, String.t()}
```

Registers an advice function for a command.

For `:before`, `:after`, and `:override`, the function has arity 1
(receives state, returns state). For `:around`, the function has
arity 2 (receives the execute function and state, returns state).

Returns `:ok` or `{:error, reason}` if the phase is invalid.

# `register`

```elixir
@spec register(atom(), phase(), atom(), function()) :: :ok | {:error, String.t()}
```

# `reset`

```elixir
@spec reset() :: :ok
```

Removes all registered advice and resets circuit breaker state.

# `reset`

```elixir
@spec reset(atom()) :: :ok
```

# `start_link`

```elixir
@spec start_link(keyword()) :: GenServer.on_start()
```

Starts the process that owns the ETS table.

# `wrap`

```elixir
@spec wrap(atom(), (map() -&gt; map())) :: (map() -&gt; map())
```

Wraps a command's execute function with all registered advice.

Returns a function `(state -> state)` that applies before advice,
then the (possibly around-wrapped or overridden) command, then
after advice.

This is the main integration point called by `dispatch_command`.

# `wrap`

```elixir
@spec wrap(atom(), atom(), (map() -&gt; map())) :: (map() -&gt; map())
```

---

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