A collection of Micro editor plugins: format, configdel, and jsonschema. Each plugin is a standalone Lua file and help doc under plugins/<name>/.
- No test framework — plugins are Lua scripts for Micro editor; no test runner exists. Validate by loading in Micro and exercising the feature manually.
- No build step — plugins are plain Lua; no compilation.
- Release automation:
release-pleasemanages plugin version bumps and GitHub releases from Conventional Commits. Each plugin has its ownversion.txtandCHANGELOG.mdunderplugins/<name>/. - Manual lint: none configured. The Lua code is not linted.
Each plugin under plugins/<name>/ follows this structure:
plugins/<name>/
<name>.lua # Main plugin code
repo.json # Plugin metadata for Micro's plugin manager
version.txt # Canonical version for release-please's simple strategy
CHANGELOG.md # Per-plugin changelog maintained by release-please
help/
<name>.md # Micro help file (registered via config.AddRuntimeFile)
The top-level repo.json is kept for backward compatibility but only exposes the format plugin (Micro resolves plugins[0] only). The channel.json file is the proper install channel — it points to each plugin's per-plugin repo.json.
repo.json format: Array of plugin objects. Each Version entry must include a Url field pointing to a downloadable .zip archive for that plugin version. Micro's plugin manager downloads and unpacks that archive; a raw .lua URL is not installable.
Plugins use Micro's embedded Lua API with these imports:
local micro = import("micro")
local config = import("micro/config")
local shell = import("micro/shell")
local buffer = import("micro/buffer")
local filepath = import("path/filepath")
local strings = import("strings")
local os = import("os")Every plugin must export:
init()— Register options, commands, and help files. Called once at plugin load.onSave(bp)— Called on every buffer save. Must returntrue. Options frombp.Buf.Settings.
function init()
config.RegisterCommonOption("pluginname", "onsave", true)
config.MakeCommand("command-name", handler_func, config.NoComplete)
config.AddRuntimeFile("pluginname", config.RTHelp, "help/pluginname.md")
endPlugin settings are accessed from bp.Buf.Settings["pluginname.key"]. Defaults are registered via config.RegisterCommonOption. The setting_enabled helper (in jsonschema) safely resolves with a fallback default.
- User-facing errors:
micro.InfoBar():Error("pluginname: message") - Success messages:
micro.InfoBar():Message("pluginname: message") - Info logging:
micro.Log(message) - Plugin name prefix on all messages (lowercase, no trailing space)
- Errors bubble up through
tostring(err)fromshell.ExecCommand
Each plugin requires external CLI tools. Resolution follows two strategies:
- Local-first: check project-local paths (
node_modules/.bin/,vendor/bin/,.venv/bin/,venv/bin/) - Fallback: search
$PATHviaos.Getenv("PATH")andos.Stat() - Upward search:
find_upwardstraverses parent directories from the file's directory looking for local binaries
Relevant external tools per plugin:
- format:
oxfmt,ecs,gofmt,ruff,black,stylua,shfmt,rustfmt(varies by file type) - configdel:
yqv4+ - jsonschema: Sourcemeta
jsonschemaCLI
Plugins that modify files externally (format, configdel) call bp.Buf:ReOpen() to reload the buffer from disk. The buffer must be saved first (bp:Save()). On-save handlers run the action silently and reopen after.
- Cursor
Yis 0-indexed line,Xis 0-indexed column - When passing to external tools or displaying, add 1 (
cursor.Y + 1,cursor.X + 1) bp.Buf:Line(n)uses 0-indexed lines
The jsonschema plugin uses buffer.NewMessage and buffer.NewMessageAtLine to surface validation errors as Micro buffer diagnostics. Messages use diagnostic_owner = "jsonschema" for scoped clearing via buf:ClearMessages(diagnostic_owner).
The jsonschema plugin implements an embedded JSON parser for building a pointer-to-location map. This maps JSON Pointer paths to line/col positions in the buffer to pin diagnostic locations accurately.
- VERSION constant at top of each
.luafile:VERSION = "x.y.z" repo.jsonversion lives atdata[0].Versions[0].Versionplugins/<name>/version.txtis the canonical version file forrelease-pleasebun scripts/sync-plugin-versions.tssyncs each pluginVERSIONconstant andrepo.jsonfromversion.txt- The top-level
repo.jsonis additionally synced from theformatplugin because it is the backward-compatible single-plugin entrypoint - Build fresh release archives with
bun scripts/package-plugin-releases.tswhen you need a local artifact preview; the script writes todist/plugin-releases/ - The GitHub Actions
release-pleaseworkflow runs the sync script, commits the generated metadata if needed, then uploads release zip assets in the same workflow run
bp.Buf:ReOpen() is used after external file modification. This is non-obvious — the buffer picks up the on-disk changes. Always save first via bp:Save(), then run the external command, then ReOpen().
The configdel plugin shows the defensive binding pattern used:
local bound = false
if config.BindKey ~= nil then
local ok = pcall(config.BindKey, "Alt-d", "command:del-key", false)
bound = ok
end
if not bound then
config.TryBindKey("Alt-d", "command:del-key", false)
endThis handles API evolution across Micro versions — BindKey was added later, TryBindKey is the older fallback.
See testdata/jsonschema/ for example files used to manually test schema validation. No automated tests exist — testing is done by loading plugins in Micro and running commands or saving files.
Defined in mise.toml:
jsonschema— latestbun— latest