Behaviour and DSL for Minga editor extensions.
Extensions are self-contained Elixir modules that add functionality to the editor. Each extension runs under its own supervisor, so a crash in one extension never affects others or the editor itself.
Implementing an extension
defmodule MingaOrg do
use Minga.Extension
option :conceal, :boolean,
default: true,
description: "Hide markup delimiters and show styled content"
option :todo_keywords, :string_list,
default: ["TODO", "DONE"],
description: "TODO keyword cycle sequence"
command :org_cycle_todo, "Cycle TODO keyword",
execute: {MingaOrg.Todo, :cycle},
requires_buffer: true
command :org_toggle_checkbox, "Toggle checkbox",
execute: {MingaOrg.Checkbox, :toggle},
requires_buffer: true
keybind :normal, "SPC m t", :org_cycle_todo, "Cycle TODO", filetype: :org
keybind :normal, "SPC m x", :org_toggle_checkbox, "Toggle checkbox", filetype: :org
@impl true
def name, do: :minga_org
@impl true
def description, do: "Org-mode support"
@impl true
def version, do: "0.1.0"
@impl true
def init(_config), do: {:ok, %{}}
endCommands and keybindings declared with command/3 and keybind/4 are
auto-registered by the framework when the extension loads. Extensions
that need runtime-dynamic commands can still call
Minga.Command.Registry.register/4 and Minga.Keymap.Active.bind/5
directly from init/1.
Config declaration
use Minga.Config
extension :minga_org, git: "https://github.com/jsmestad/minga-org",
conceal: false,
pretty_bullets: trueOptions declared with option/3 are validated against their type at
load time. Users get clear errors for type mismatches. Unknown keys
produce a warning log.
Reading options at runtime
Minga.Config.get_extension_option(:minga_org, :conceal)
# => falseLifecycle
- The extension module is compiled from the declared path
- Options from the extension declaration are validated against the schema
init/1is called with the config keyword list (minus:path)- The extension's
child_spec/1is started underMinga.Extension.Supervisor - On config reload (
SPC h r), all extensions are stopped and re-loaded
Summary
Types
Vim modes that extensions can bind keys in.
A single command specification: {name, description, opts}.
Extension metadata and runtime info. See Minga.Extension.Entry.
Extension runtime status.
A single keybinding specification: {mode, key_string, command, description, opts}.
A single option specification: {name, type, default, description}.
Callbacks
Optional. Returns a child spec for the extension's supervision subtree.
A short human-readable description of what the extension does.
Called when the extension is loaded. Receives the config keyword list
from the extension declaration (with source keys like :path removed).
The extension's unique name (atom).
The extension's version string (e.g. "0.1.0").
Functions
Injects the Minga.Extension behaviour, DSL macros (option/3,
command/3, keybind/4, keybind/5), and a default child_spec/1.
Declares an editor command this extension provides.
Declares a keybinding this extension provides.
Declares a typed config option for this extension.
Types
@type bindable_mode() :: :normal | :insert | :visual | :operator_pending
Vim modes that extensions can bind keys in.
Extensions bindable modes are the user-facing editing modes. Internal
modes like :search_prompt, :substitute_confirm, and :extension_confirm
are framework internals that extensions should not bind into.
A single command specification: {name, description, opts}.
The opts keyword list supports:
:execute(required) —{Module, :function}MFA tuple. The function receives editor state and returns new state. Extensions that need config should callMinga.Config.Options.get_extension_option/2inside the function body.:requires_buffer— whentrue, command is skipped if no buffer is active (default:false)
@type extension_info() :: Minga.Extension.Entry.t()
Extension metadata and runtime info. See Minga.Extension.Entry.
@type extension_status() :: :running | :stopped | :crashed | :load_error
Extension runtime status.
@type keybind_spec() :: {bindable_mode(), String.t(), atom(), String.t(), keyword()}
A single keybinding specification: {mode, key_string, command, description, opts}.
The mode must be a bindable_mode (:normal, :insert, :visual,
or :operator_pending). The key string uses the same format as
Minga.Keymap.Active.bind/5 (e.g. "SPC m t", "M-h", "TAB").
Opts supports :filetype for scoping.
@type option_spec() :: {atom(), Minga.Config.type_descriptor(), term(), description :: String.t()}
A single option specification: {name, type, default, description}.
The type descriptor uses the same types as Minga.Config.Options:
:boolean, :pos_integer, :string, :string_list, {:enum, [atoms]}, etc.
The doc string is used by SPC h v (describe option) and other
introspection features.
Callbacks
@callback child_spec(config :: keyword()) :: Supervisor.child_spec()
Optional. Returns a child spec for the extension's supervision subtree.
The default implementation (provided by use Minga.Extension) starts
a simple Agent that holds the state returned by init/1. Override this
if your extension needs a custom GenServer, multiple processes, or a
full supervision tree.
@callback description() :: String.t()
A short human-readable description of what the extension does.
Called when the extension is loaded. Receives the config keyword list
from the extension declaration (with source keys like :path removed).
Return {:ok, state} to start successfully, or {:error, reason} to
report a load failure without crashing the editor.
@callback name() :: atom()
The extension's unique name (atom).
@callback version() :: String.t()
The extension's version string (e.g. "0.1.0").
Functions
Injects the Minga.Extension behaviour, DSL macros (option/3,
command/3, keybind/4, keybind/5), and a default child_spec/1.
The option macro
Declares a typed config option the extension accepts:
option :conceal, :boolean, default: true
option :heading_bullets, :string_list, default: ["◉", "○", "◈", "◇"]At compile time, these are accumulated into __option_schema__/0,
a generated function the framework reads at load time to validate
user config and register options in ETS.
The command macro
Declares an editor command the extension provides:
command :org_cycle_todo, "Cycle TODO keyword",
execute: {MingaOrg.Todo, :cycle},
requires_buffer: trueAccumulated into __command_schema__/0. The framework registers
these in Minga.Command.Registry when the extension loads. The
execute MFA must be a {Module, :function} tuple whose function
accepts editor state and returns new state.
The keybind macro
Declares a keybinding the extension provides:
keybind :normal, "SPC m t", :org_cycle_todo, "Cycle TODO", filetype: :orgAccumulated into __keybind_schema__/0. The framework registers
these in Minga.Keymap.Active when the extension loads.
Supported option types
:boolean, :pos_integer, :non_neg_integer, :integer, :string,
:string_or_nil, :string_list, :atom, {:enum, [atoms]},
:map_or_nil, :any.
Declares an editor command this extension provides.
Accumulated at compile time and exposed via __command_schema__/0.
The framework auto-registers these commands when the extension loads.
Options
:execute(required) —{Module, :function}MFA tuple. The function receives editor state and returns new state.:requires_buffer— whentrue, command is skipped if no buffer is active (default:false)
Examples
command :org_cycle_todo, "Cycle TODO keyword",
execute: {MingaOrg.Todo, :cycle},
requires_buffer: true
command :org_toggle_checkbox, "Toggle checkbox",
execute: {MingaOrg.Checkbox, :toggle},
requires_buffer: true
Declares a keybinding this extension provides.
Accumulated at compile time and exposed via __keybind_schema__/0.
The framework auto-registers these keybindings when the extension loads.
Examples
keybind :normal, "SPC m t", :org_cycle_todo, "Cycle TODO"
keybind :normal, "M-h", :org_promote_heading, "Promote heading", filetype: :org
Declares a typed config option for this extension.
Accumulated at compile time and exposed via __option_schema__/0.
Options
:default(required) — the default value when the user doesn't set it:description(required) — a short human-readable description shown bySPC h v
Examples
option :conceal, :boolean,
default: true,
description: "Hide markup delimiters and show styled content"
option :format, {:enum, [:html, :pdf, :md]},
default: :html,
description: "Default export format"
option :heading_bullets, :string_list,
default: ["◉", "○"],
description: "Unicode bullets for heading levels (cycles when depth exceeds list length)"