pmacs/builtin/hooks/default.lua

136 lines
6.2 KiB
Lua

-- builtin/hooks/default.lua --- M2.6 + M4.12 lifecycle hooks.
--
-- Defines the named hooks the editor itself fires:
-- * buffer.before-save --- short-circuit. A callback may veto the
-- save by returning false (e.g. a linter
-- that refuses to save while errors exist).
-- * buffer.after-load --- all-must-succeed. Every listener runs
-- when a file loads; failures are logged
-- to *errors* but don't block the others.
-- * buffer.after-edit --- all-must-succeed (M4.12). Fired after a
-- key dispatch that mutated the active
-- buffer; LSP did_change wiring debounces
-- this. Listeners receive no arguments.
-- * buffer.after-save --- all-must-succeed (M4.12). Fired after a
-- successful save; LSP did_save and
-- format-on-save subscribe here.
-- * editor.before-quit --- short-circuit. A callback may veto quit
-- (e.g. "buffer modified --- save first?").
-- * resource.renamed --- all-must-succeed (dired Stage 2a). Fired
-- after a successful rename, with (old, new)
-- canonical absolute paths.
-- * resource.deleted --- all-must-succeed (dired Stage 2a). Fired
-- after a successful delete, with the
-- canonical absolute path.
--
-- These are *defined* here so user config can attach callbacks via
-- pmacs.hook.add. Run sites are in Rust (after-load, after-edit) and in
-- builtin/commands/default.lua (before-save, after-save, before-quit).
local define = pmacs.hook.define
define {
name = "buffer.before-save",
description = "Fired before pmacs.editor.save writes the active buffer to disk. " ..
"Return false to veto the save.",
kind = "short-circuit",
}
define {
name = "buffer.after-load",
description = "Fired after a file is read into a buffer. Listeners " ..
"run independently; one failure does not block the rest.",
kind = "all-must-succeed",
}
define {
name = "buffer.after-edit",
description = "Fired after a key dispatch that modifies the active buffer. " ..
"LSP did_change subscribers debounce this hook.",
kind = "all-must-succeed",
}
define {
name = "buffer.after-switch",
description = "Fired after the active window switches to a different, " ..
"already-open buffer (C-x b, panel visits, find_or_open of " ..
"an open file). Switching clears the window's overlays; " ..
"syntax/LSP subscribers re-attach theirs here. Fresh loads " ..
"fire buffer.after-load instead.",
kind = "all-must-succeed",
}
define {
name = "buffer.after-save",
description = "Fired after a successful save. LSP did_save and " ..
"format-on-save subscribers fire here.",
kind = "all-must-succeed",
}
define {
name = "path.open-directory",
description = "Fired when a directory path is opened (Journey Stage 1a). " ..
"Receives the canonical absolute path and an opaque " ..
"destination. Return false to CLAIM the directory and stop " ..
"the fan-out; return nothing to decline. No builtin " ..
"subscribes -- because hook callbacks only ever append, a " ..
"subscribing builtin would always claim before any user " ..
"listener could run, so this hook is the user's chain and " ..
"pmacs.path.directory_handler is the default surface it " ..
"falls back to. A callback that RAISES stops the chain and " ..
"suppresses that fallback.",
kind = "short-circuit",
}
define {
name = "resource.renamed",
description = "Fired once per SUCCESSFUL filesystem rename, with the old " ..
"and new paths as canonical absolute strings. The core " ..
"reconciles what it can reach -- buffer paths and names, the " ..
"URI-keyed LSP stores, attached diagnostic overlays -- but a " ..
"package that keys its own state by path or URI is invisible " ..
"to that, so this hook is the mechanism that scales. It " ..
"carries PATHS rather than a rebind list precisely because " ..
"dired's listing buffers are pathless: a path-keyed consumer " ..
"must be able to reconcile from (old, new) alone. Does not " ..
"fire for a rename that failed or was cancelled.",
kind = "all-must-succeed",
}
define {
name = "resource.deleted",
description = "Fired once per SUCCESSFUL filesystem delete, with the " ..
"canonical absolute path. Buffers on the path and beneath it " ..
"have already been reconciled: unmodified ones killed " ..
"through both removal phases, modified ones kept alive. " ..
"Subscribers drop their own path-keyed state. Does not fire " ..
"for a delete that failed or was cancelled.",
kind = "all-must-succeed",
}
define {
name = "editor.before-quit",
description = "Fired before the editor exits. Return false to veto.",
kind = "short-circuit",
}
define {
name = "frontend.detached",
description = "Fired when an attached frontend's session ends. " ..
"Receives the raw frontend id (integer). Modules keying " ..
"state by pmacs.frontend.id() (kill-ring sessions) drop " ..
"that id's entries here (Q#KR11).",
kind = "all-must-succeed",
}
define {
name = "process.after-tick",
description = "Fired once per editor frame, immediately after the process " ..
"supervisor's tick drains pending I/O and exit events. " ..
"Subscribers typically call pmacs.process.events_take(id) " ..
"to drain events for processes they care about. The hook " ..
"fires unconditionally each frame; an empty supervisor still " ..
"fires the hook (subscribers carry their own registries).",
kind = "all-must-succeed",
}