feat(buffer): open as bottom-drawer split like fugitive (#23)

* feat(buffer): open as bottom-drawer split like fugitive

Problem: :Pending replaced the current buffer, making it impossible to
view tasks alongside the file being edited. No way to close the drawer
without :q or switching buffers manually.

Solution: open the task buffer in a botright horizontal split instead of
replacing the current buffer. Track the drawer window ID so re-opening
focuses it rather than creating a second split. Set winfixheight so the
drawer keeps its height when other windows open or close. Add q/<Esc>
mappings to close the drawer, and a WinClosed autocmd to clear the
tracked window ID when the user closes it manually. Add drawer_height
config option (default 15).

* fix(buffer): default to natural split height like fugitive

Problem: hardcoded drawer_height=15 was too small and diverged from
fugitive's model. Fugitive issues a plain botright split and lets Vim's
own split rules (equalalways, winheight) divide the available space.

Solution: remove the default height so the split sizes naturally. Only
call nvim_win_set_height when the user sets drawer_height to a positive
value, preserving the opt-in customization path.
This commit is contained in:
Barrett Ruth 2026-02-25 09:34:17 -05:00 committed by GitHub
parent 5db242a9cf
commit fbeb0e2bee
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 49 additions and 14 deletions

View file

@ -7,6 +7,8 @@ local M = {}
---@type integer?
local task_bufnr = nil
---@type integer?
local task_winid = nil
local task_ns = vim.api.nvim_create_namespace('pending')
---@type 'category'|'priority'|nil
local current_view = nil
@ -25,11 +27,27 @@ function M.bufnr()
return task_bufnr
end
---@return integer?
function M.winid()
return task_winid
end
---@return string?
function M.current_view_name()
return current_view
end
function M.clear_winid()
task_winid = nil
end
function M.close()
if task_winid and vim.api.nvim_win_is_valid(task_winid) then
vim.api.nvim_win_close(task_winid, false)
end
task_winid = nil
end
---@param bufnr integer
local function set_buf_options(bufnr)
vim.bo[bufnr].buftype = 'acwrite'
@ -50,6 +68,7 @@ local function set_win_options(winid)
vim.wo[winid].foldcolumn = '0'
vim.wo[winid].spell = false
vim.wo[winid].cursorline = true
vim.wo[winid].winfixheight = true
end
---@param bufnr integer
@ -251,24 +270,25 @@ function M.open()
setup_highlights()
store.load()
if task_bufnr and vim.api.nvim_buf_is_valid(task_bufnr) then
local wins = vim.fn.win_findbuf(task_bufnr)
if #wins > 0 then
vim.api.nvim_set_current_win(wins[1])
M.render(task_bufnr)
return task_bufnr
end
vim.api.nvim_set_current_buf(task_bufnr)
set_win_options(vim.api.nvim_get_current_win())
if task_winid and vim.api.nvim_win_is_valid(task_winid) then
vim.api.nvim_set_current_win(task_winid)
M.render(task_bufnr)
return task_bufnr
return task_bufnr --[[@as integer]]
end
task_bufnr = vim.api.nvim_create_buf(true, false)
if not (task_bufnr and vim.api.nvim_buf_is_valid(task_bufnr)) then
task_bufnr = vim.api.nvim_create_buf(true, false)
set_buf_options(task_bufnr)
end
set_buf_options(task_bufnr)
vim.api.nvim_set_current_buf(task_bufnr)
set_win_options(vim.api.nvim_get_current_win())
vim.cmd('botright new')
task_winid = vim.api.nvim_get_current_win()
vim.api.nvim_win_set_buf(task_winid, task_bufnr)
local h = config.get().drawer_height
if h and h > 0 then
vim.api.nvim_win_set_height(task_winid, h)
end
set_win_options(task_winid)
M.render(task_bufnr)

View file

@ -9,6 +9,7 @@
---@field date_format string
---@field date_syntax string
---@field category_order? string[]
---@field drawer_height? integer
---@field gcal? pending.GcalConfig
---@class pending.config

View file

@ -38,11 +38,25 @@ function M._setup_autocmds(bufnr)
end
end,
})
vim.api.nvim_create_autocmd('WinClosed', {
group = group,
callback = function(ev)
if tonumber(ev.match) == buffer.winid() then
buffer.clear_winid()
end
end,
})
end
---@param bufnr integer
function M._setup_buf_mappings(bufnr)
local opts = { buffer = bufnr, silent = true }
vim.keymap.set('n', 'q', function()
buffer.close()
end, opts)
vim.keymap.set('n', '<Esc>', function()
buffer.close()
end, opts)
vim.keymap.set('n', '<CR>', function()
M.toggle_complete()
end, opts)