#4183

plugins: add `onBufferClose()` to micro

Openusfbih8uopened 2mo ago

Description

There is no easy way to know when a Buffer is closed. If you replace it by calling BufPane:OpenBuffer() directly from a plugin, it does not trigger anything.

Activity

1
chahat-1012mo ago

I had a dig through this. I think the natural spot is Buffer.Close(), firing before Fini() and before the buffer is dropped from OpenBuffers:

func (b *Buffer) Close() {
	for i, buf := range OpenBuffers {
		if b == buf {
			err := config.RunPluginFn("onBufferClose", luar.New(ulua.L, b))
			if err != nil {
				screen.TermMessage(err)
			}

			b.Fini()
			// ... existing removal

Close() is where every real close path ends up: ForceQuit, QuitAll via CloseOpenBuffers, tab and split closes, and BufPane.OpenBuffer(), which is the case you hit. Putting it inside the if b == buf branch also means no double-fire if something closes a buffer twice.

Doing it as an onQuit action hook instead doesn't really work, for what it's worth. Quitting the last pane reaches runtime.Goexit() before the hook would run, and it wouldn't fire for OpenBuffer or :open at all.

Firing before Fini() matters because the buffer is still fully readable at that point: path, settings, contents, cursors. And firing while it's still in OpenBuffers makes buf:Shared() usable inside the callback, since Shared() skips the buffer itself. That lets a plugin tell "this view is going away" from "this file is now completely closed" rather than us picking one meaning for them.

I'd make it non-cancellable (RunPluginFn, same as onBufferOpen). By the time Close() runs the decision is already made, and a veto during QuitAll would just be ignored since shutdown continues regardless.

One gap worth naming: it won't fire on SIGTERM/SIGHUP, because exit() calls Fini() directly. I'd lean towards documenting that rather than running Lua from the panic recovery path, but happy to be told otherwise.

Two things I'm unsure about:

  • RunPluginFn or RunPluginFnBool(b.Settings, ...)? onBufferOpen ignores the per-buffer plugin setting while PluginCB honours it. I matched onBufferOpen, but I can see the other argument.
  • Should it fire for BTInfo/BTLog/BTHelp as well? onBufferOpen does, so I assumed yes, but it does mean plugins see a close for the infobar during shutdown.

If that sounds right, I'll open a PR with the hook, a plugins.md entry and a test.