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)
Summary
Types
Around advice: receives the execute function and state.
Advice phase.
Before/after/override advice: transforms editor state.
Functions
Returns true if any advice of any phase is registered for the command.
Returns true if a specific advice function has been disabled by the circuit breaker.
Returns true if any advice is registered for the given phase and command.
Registers an advice function for a command.
Removes all registered advice and resets circuit breaker state.
Starts the process that owns the ETS table.
Wraps a command's execute function with all registered advice.
Types
Around advice: receives the execute function and state.
@type phase() :: :before | :after | :around | :override
Advice phase.
Before/after/override advice: transforms editor state.
Functions
Returns true if any advice of any phase is registered for the command.
Returns true if a specific advice function has been disabled by the circuit breaker.
Returns true if any advice is registered for the given phase and command.
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.
@spec reset() :: :ok
Removes all registered advice and resets circuit breaker state.
@spec reset(atom()) :: :ok
@spec start_link(keyword()) :: GenServer.on_start()
Starts the process that owns the ETS table.
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.