#4230

Set the terminal's background colour to match the current colourscheme

Openachadwickopened 1mo ago

Description

At present, Micro does not attempt to set the terminal background to match the current colorscheme's normal background colour. If the background colour that Micro uses is very different from the terminal's normal background colour, it can result in large and inconsistent gaps where the terminal's background is shown, and not the editor's. It would be prettier and more consistent if we could get those gaps to match Micro's own background.

This screenshot is exaggerated for effect, and the right hand side is a mock-up of what I'm hoping we can have. The size and placement of the gaps depends on how your terminal emulator distributes extra whitespace, how much default border you configure in it, and also how well your window manager or compositor respects incremental sizing requests.

So the feature request is can we have a way to make those borders the same colour as the background colour used by Micro, when using a truecolor theme? There's more than one way to do it, I guess.

  • I was considering writing a plugin for it, after successfully making one for myself to set the xterm-style window title. Back when I was still using NeoVim, I used bg.nvim on a daily basis, and it mostly did a fair job. A plugin could do this job if it has access to the truecolor style info from the current colorscheme, the ability to add callbacks that fire after changing styles, and maybe a tty access layer (although, I just used io.write() to spray xterm codes back at the terminal for my title-setting thing!) Sadly, GetColor does not seem to be wrapped for Lua; something like it has been suggested in #3465 though.

  • I noticed that Helix editor does this by default, with its standard settings. Perhaps it is done as part of its low-level character cell styling routines? Micro's tcell might be the best place for this, then, and if it's implemented there, perhaps it should be made the default behaviour in Micro as a whole if it can be made reliable enough (the terminal needs to support it, but I'm not not sure how you'd determine that capability)?

The colour will need to be reset upon leaving Micro, however it's done.

Activity

5
Andriamanitra1mo ago

I copied the code from bg.nvim (had to tweak it a bit to work on our version of Lua) and it seems to work with some caveats:

  • As you pointed out there is no nice way for plugins to get the colors from the current colorscheme so I had to hardcode the color.
  • It's tricky to get the reset code to run when the editor is being closed because we don't have an event hook for that. I used preQuit but that runs every time a pane is closed so if you use multiple tabs / splits it will reset the background color prematurely.

We could extend the plugin API to fix the above issues but I reckon we should rather implement this feature in micro itself.

~/.config/micro/init.lua

local BG_COLOR = "#FF0000" -- hardcoded to match your color scheme

local update_count = 0
local resetBg = function()
    if os.getenv("TMUX") then
        io.write("\27Ptmux;\27\27]111\07\27\\")
    elseif os.getenv("TERM") == "xterm-kitty" then
        for _ = 1, update_count do
            io.write("\27]30101\07")
        end
    else
        io.write("\27]111\07")
    end
    io.flush()
end

function changeBg(hex)
    if hex == nil then return reset() end

    if os.getenv("TERM") == "xterm-kitty" then
        io.write("\27]30001\07")
    end

    if os.getenv("TMUX") then
        io.write("\27Ptmux;\27\27]11;" .. hex .. "\07\27\\")
    else
        io.write("\27]11;" .. hex .. "\07")
    end
    io.flush()

    update_count = update_count + 1
end

function init()
    changeBg(BG_COLOR)
end

-- FIXME: this should only run if the closed BufPane is the last one
function preQuit()
    changeBg(nil)
end
achadwick1mo ago

Thoughts on doing it in a plugin (I agree, I don't think it's right at this level)

Shame that deinit() doesn't fire before Micro exits. That would feel like a natural place to hook cleanup code. I guess we can count panes if needed, and generally turn off the lights if we're about to leave the house through the known doors...

function preQuit()
	local panecount = 0
	local tabs = micro.Tabs()
	for i = 1, #tabs.List do
		panecount = panecount + #tabs.List[i].Panes
	end
	if panecount == 1 then
		resetBg()
	end
end

function preQuitAll()
	resetBg()
end

function deinit()   -- doesn't fire before exit :( sadface
	resetBg()
end

... but I'd really want something more reliable like a proper micro.AtExit() alongside micro.After(), or an assurance that plugins deinit() just before Micro exits (have to workshop precisely when, tho).

There's a few internal corner cases to consider too, like whether we're about to go into a shell.RunInteractiveShell(). Since those shells are not styled by Micro, they probably ought to use the shell's own background colour (consider when Micro uses a dark-bg colorscheme, but the terminal it's running in has a light-bg theme as an extreme but valid example). Not shell.RunTermEmulator() shells, though - those run within Micro panes, and are styled by it (or rather... they're not, as far as my fzf hack plugins can determine).

But yeah, that's a complete aside.

It would be really nice and useful to have colour scheme introspection and proper at-exit cleanup hooks for Lua plugins, but perhaps for this it would be better to implement it at the golang layers that abstract out terminal capabilities and the colour schemes themselves.

achadwick1mo ago

Those control sequences

The xterm-style ones are documented as the "Change VT100 text background color" sequence and its query counterpart with "?" here in the giant list of XTerm control sequences1.

EDIT: even more maddeningly, if a hypothetical bg.micro plugin wanted to support 16-colour, and 256-colour colorschemes by itself, that could require use of the unsettling and uncanny ? query counterpart of the "Change Color Number c to the color specified by spec" Xterm control sequence. All issued out of band, and outside of any state tracking that tcell does, and handling the different result flavours you get back from different terminal emulators. Of course, that's only if the colorscheme overrides default in the first place! That's a hell of a lot of logic and tty business for a Lua plugin to implement by itself. Best implemented within Micro's golang internals, I reckon: a full Lua plugin would need at the very least tcell and GetColor() wraps.

I don't know what those xterm-kitty and TMUX extras and alternative sequences are targeting. Presumably external multi-pane stuff, but since I don't use them much, I haven't tested with them. Presumably the bg.nvim repo has more info on why that's happening.

Footnotes

  1. https://www.xfree86.org/current/ctlseqs.html (alt link with expired cert) ↩

JoeKar1mo ago

Micro's tcell might be the best place for this, then, and if it's implemented there, [...]

Right, this terminal escape sequence stuff is something tcell should handle instead of micro. Using a plugin to do this is just a workaround.

Shame that deinit() doesn't fire before Micro exits.

That is indeed something to address. -> #4231

achadwick1mo ago

Thanks for the PR on the deinit/cleanup stuff!

this terminal escape sequence stuff is something tcell should handle instead of micro.

Aha, tcell is an external dependency. I thought it was part of micro itself, so apologies for that. Hopefully it's something that can be done with the tcell dependency as it stands rather than something that needs to be added to tcell first. My time and the joy I feel with open source projects is pretty limited these days, so I don't really care to waste time chasing this with tcell's upstream if they've really started ingesting LLM slop on the regular.

I hope somebody likes the idea of this enough to implement. It will need knowledge of colorschemes at the micro-editor golang level anyway, so best leave this feature request open till something emerges.