Events¶
Plugins react to editor activity through IEventBus. Events are plain structs
defined in PluginEvents.h:
| Event | Payload |
|---|---|
DocumentOpened |
DocumentId, std::filesystem::path |
DocumentAboutToClose |
DocumentId — the user is closing the document, and the close is certain; published just before its last view goes, while it is still fully readable. Not on quit. Native plugins only; see below |
DocumentClosed |
DocumentId |
DocumentSaved |
DocumentId |
DocumentPrinted |
DocumentId, pages — published after a print job reached the device (File ▸ Print, or Print inside the preview); pages counts the document pages printed, fewer than the document has when a selection or page range was chosen. A cancelled dialog publishes nothing. C++ plugins only for now |
DocumentAboutToSave |
DocumentId, path — synchronous, buffer still editable (trim whitespace, add final newline); no dialogs |
DocumentEncodingChanged |
DocumentId, DocumentEncoding — the encoding the host will write the document back in changed (file just identified, or setDocumentEncoding called) |
DocumentLineEndingChanged |
DocumentId, LineEnding — the line terminator the document uses changed (file just read or reloaded, an .editorconfig end_of_line applied on save, or setLineEnding called) |
DocumentModifiedChanged |
DocumentId, modified — the clean/dirty flag toggled (not every keystroke) |
TextChanged |
DocumentId, pos, addedBytes, removedBytes |
SelectionChanged |
DocumentId |
ActiveDocumentChanged |
DocumentId |
HostWindowShown |
(none) — once per run, after the first frame is on screen; schedule slow startup work off this |
SettingsChanged |
scope — "" = editor-wide, "plugins/<id>" = that plugin's page was applied |
WorkspaceRootChanged |
root (empty = folder closed) — published by the Workspace plugin |
WorkspaceFilesChanged |
files[] of {path, displayPath} — published by the Workspace plugin |
ThemeChanged |
variant ("light" / "dark" / "") |
AppAboutToQuit |
(none) |
CommandShortcutChanged |
commandId, sequence (new portable chord; "" = unbound) |
MarginClicked |
DocumentId, line, margin (EditorMargin::Symbol), modifiers — a left click in the bookmark column of an editor, on every such click; see Line markers |
Plugins can publish events too (ctx.events().publish(e)) — that is how
the Workspace plugin broadcasts WorkspaceRootChanged /
WorkspaceFilesChanged for Find in Files and the Command Palette to consume.
One document, one id¶
A DocumentId names a document, not a pane. Split Document shows one
file in two panes, and both panes carry the same DocumentId, so:
DocumentOpenedis published once per file — splitting publishes nothing;DocumentClosedis published only when the last view of the document closes, so closing one pane never tears down state the other still needs;activeDocument()answers with that one id whichever pane is in front, andopenDocuments()lists the document once.
There is deliberately no per-pane event and no way to ask which pane is active: panes are a host concern, and everything a plugin can observe — text, caret, selection, encoding, line ending, modified state — belongs to the document, which the panes share.
DocumentAboutToClose¶
Published once per document, immediately before its last view goes away,
when the close is certain — any save prompt was answered and not cancelled.
Inside the handler the document is still whole: text(), filePath(),
caret(), bookmarkedLines() and the encoding all answer. The teardown
follows, then DocumentClosed. Every way of closing a tab publishes it (Close,
Close All, Close Others / to the Right, a middle-click, Close Folder, a plugin's
closeDocument()), untitled documents included; closing one pane of a document
still shown in another does not.
Quitting does not publish it. The application publishes AppAboutToQuit
once, while every document is still open, and no per-document close events: a
plugin that keeps per-document state across sessions walks openDocuments() in
its AppAboutToQuit handler. DocumentAboutToClose therefore always means
"this document is being closed", never "the program is exiting".
Handlers run synchronously, inside the close: be fast, show no dialogs, and treat the set of documents as read-only — do not open, close or activate documents, and do not edit the closing document's text.
It is native-only by design: delivery to Python plugins is asynchronous, so the "still readable" promise could not be kept there, and the event is not offered to them (see Python events).
CommandShortcutChanged¶
CommandShortcutChanged fires when a command's effective shortcut changes —
a user remap in the Shortcut Mapper, a keymap import/reset, or a conflict
suppression engaging/lifting. Menu-bound commands need nothing (the host
rebinds their action); commands registered with
scope = command_scope::kPluginWindow subscribe and re-apply the sequence to
their own widgets (see Commands & menus).
Subscribing¶
subscribe is a template that returns a Subscription (RAII). Keep the
subscription alive as a member for as long as you want the callback to fire;
destroying it unsubscribes.
m_sub = ctx.events().subscribe<MTE::plugin::DocumentSaved>(
[this](const MTE::plugin::DocumentSaved& e) {
// react to e.id ...
});
class IEventBus {
public:
template <class Event>
Subscription subscribe(std::function<void(const Event&)> cb);
// Forwards to a type-erased subscribeRaw(std::type_index, ...) internally.
};
Tip
Hold every Subscription as a plugin member so all callbacks are torn down
automatically in shutdown().