center.lua
Branches > main > e8bdc19 > files / nvim / lua / center.lua
--- Keeps the current line centered on the screen always,
--- including when at the top of a buffer.
--- It does this by adding a bunch of fake virtual lines above the first line of the buf,
--- and scrolling the screen to the correct place
local api = vim.api
local ac = vim.api.nvim_create_autocmd
local ag = vim.api.nvim_create_augroup
local fn = vim.fn
local ns = api.nvim_create_namespace("dev.grahp.nvim.padding_lines")
local function set_padding_lines(lines)
api.nvim_buf_clear_namespace(0, ns, 0, -1)
api.nvim_buf_set_extmark(0, ns, 0, 0, {
virt_lines = lines,
virt_lines_above = true
})
end
--- Needed for first paint otherwise it'd be in utils :p
local function replicate(n, x)
local tbl = {}
if n > 0 then
for i = 1, n do
table.insert(tbl, x)
end
end
return tbl
end
--- Centers the cursor on the screen
--- Normally, the screen cannot go above the first line of the buffer,
--- so this adds enough virtual lines above the top of the buffer to allow you to scroll past the top of the buffer
--- Centers the screen, and then feeds enough <C-y>s to scroll the cursor to the very center of the screen
--- If you're completely past the top of the buffer, just `zz` works and it won't scroll anything.
local function center()
local win_height = api.nvim_win_get_height(0)
local padding_lines = math.ceil(win_height / 2)
local blank_line = {{""}}
set_padding_lines(replicate(win_height, blank_line))
local _, lnum, col, off, curswant = unpack(fn.getcurpos())
--- TODO explain this schedule
vim.schedule(function()
vim.cmd("norm! zz")
local line_number = fn.line(".")
local offset = padding_lines - line_number + 1
if offset > 0 then
vim.cmd("norm! " .. offset .. "")
end
-- Reset cursor back to the column it was at before
fn.cursor({vim.fn.getcurpos()[2], col, off, curswant})
end)
end
--- When moving the cursor, entering a buffer, etc., center the screen
ag("TopPad", {clear = true})
ac({ "BufWinEnter", "BufEnter", "WinResized", "WinScrolled", "CursorMoved", "CursorMovedI"}, {
group = "TopPad",
callback = center,
})