Skip to content

Kitty Nvim

Kitty terminal screen scrollback using NeoVim ​

What is terminal screen scrollback ​

The screen scrollback (or scrollback buffer) is a dedicated memory storage that captures text and output that has scrolled off the top of your active terminal window.

Open Scrollback in Nvim ​

In Kitty, you have the option to open the scrollback using Nvim - it is a convenient way to copy some text from your screen.

  • Nvim has to open fast so it is a good idea to have a minimum set of Nvim plugins loaded.
  • It is important to reproduce the same visual experience of the terminal screen in the editor (colours, fonts, icons) so you can seamlessly find what you need to copy.
  • It is crucial to have a slightly different theme in the editor so you can tell you are not in the terminal (and won't start typing commands in the editor).
  • Exiting the editor should be as easy/quick as to open: just press 'q' or 'esc' without asking for saving a file.

Setting up Kitty ​

In your config file (usually at ~/.config/kitty/kitty.conf), add:

config
map kitty_mod+h launch --stdin-source=@screen_scrollback --stdin-add-formatting --type=overlay zsh -c "SCROLL_MODE=2 nvim -c 'silent write! /tmp/kitty_scrollback_buffer | te cat /tmp/kitty_scrollback_buffer - ' -c 'set modifiable' -c 'normal! G' -c 'set number' -c 'set relativenumber' "

Here I'm setting kitty_mod (that in my setup is 'alt+crtl') plus 'h' to open the scrollback - the remaining until the lonely ' - ' is to retain the current terminal screen layout in the editor. Afterwards we have some Nvim instructions, most being the 'normal! G', that sets the cursor to the end of the buffer.

Why Nvim options here? ​

For some reason, some options won't work when set in the Nvim config itself and have to passed this way.

Setting up Nvim ​

In your Nvim config (~/.config/nvim/init.lua):

lua
-- Minimal config for Kitty scroll mode - Kitty config has more options:
if os.getenv("SCROLL_MODE") then
	vim.keymap.set({ "n", "v" }, "q", "<Cmd>qa!<CR>")
	vim.keymap.set({ "n", "v" }, "<ESC>", "<Cmd>qa!<CR>")
	vim.keymap.set({ "n", "v" }, "yy", "yy<Cmd>qa!<Cr>")
	vim.cmd([[
    colorscheme wildcharm
    set clipboard=unnamed cursorline cursorlineopt=both
    set laststatus=0 nonu nornu signcolumn=no cmdheight=0
  ]])
	vim.api.nvim_create_autocmd("UIEnter", { -- I tried all possible events
		once = true,
		callback = function()
			vim.schedule(function()
				local lines = vim.api.nvim_buf_get_lines(0, 0, -1, false)
				for i, line in ipairs(lines) do
					lines[i] = line:gsub("\r$", "")
				end

				while #lines > 0 and lines[#lines]:match("^%s*$") do
					table.remove(lines)
				end
				vim.api.nvim_buf_set_lines(0, 0, -1, false, lines)

				vim.print("Started!") -- Works!
			end)
		end,
	})
	-- vim.cmd("%s/\\s*\\n$//ge") -- Doesn't work, even in the callback
	-- vim.cmd("g/^$/d") -- Doesn't work, even in the callback
	return
end

This is a minimum setup what will be used by Nvim only when in scrollback mode: I'm setting some keymaps to leave the editor quickly, a colorscheme what is different from the one I'm using, the clipboard to copy content and disabling some nvim components.

The nvim_create_autocmd is there to try to get rid of all empty lines at the end of the buffer - but it doesn't work. Neither does the commented vim.cmd.

Regardless of the empty lines setback, this approach for a Kitty scrollback is quite convenient.