From b379b3cf13143e57460467a45596e59f3a37b579 Mon Sep 17 00:00:00 2001 From: Barrett Ruth Date: Wed, 25 Feb 2026 09:24:04 -0500 Subject: [PATCH 1/2] 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/ 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). --- lua/pending/buffer.lua | 45 +++++++++++++++++++++++++++++------------- lua/pending/config.lua | 2 ++ lua/pending/init.lua | 14 +++++++++++++ 3 files changed, 47 insertions(+), 14 deletions(-) diff --git a/lua/pending/buffer.lua b/lua/pending/buffer.lua index 8f9fb59..1dc4e9d 100644 --- a/lua/pending/buffer.lua +++ b/lua/pending/buffer.lua @@ -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,22 @@ 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) + vim.api.nvim_win_set_height(task_winid, config.get().drawer_height) + set_win_options(task_winid) M.render(task_bufnr) diff --git a/lua/pending/config.lua b/lua/pending/config.lua index 2e647e4..5997994 100644 --- a/lua/pending/config.lua +++ b/lua/pending/config.lua @@ -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 @@ -22,6 +23,7 @@ local defaults = { date_format = '%b %d', date_syntax = 'due', category_order = {}, + drawer_height = 15, } ---@type pending.Config? diff --git a/lua/pending/init.lua b/lua/pending/init.lua index ec69d89..14b9c24 100644 --- a/lua/pending/init.lua +++ b/lua/pending/init.lua @@ -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', '', function() + buffer.close() + end, opts) vim.keymap.set('n', '', function() M.toggle_complete() end, opts) From baa5444a7e6cceef8d40599017012d9e05cacfd0 Mon Sep 17 00:00:00 2001 From: Barrett Ruth Date: Wed, 25 Feb 2026 09:32:00 -0500 Subject: [PATCH 2/2] 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. --- lua/pending/buffer.lua | 5 ++++- lua/pending/config.lua | 3 +-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/lua/pending/buffer.lua b/lua/pending/buffer.lua index 1dc4e9d..d11254b 100644 --- a/lua/pending/buffer.lua +++ b/lua/pending/buffer.lua @@ -284,7 +284,10 @@ function M.open() vim.cmd('botright new') task_winid = vim.api.nvim_get_current_win() vim.api.nvim_win_set_buf(task_winid, task_bufnr) - vim.api.nvim_win_set_height(task_winid, config.get().drawer_height) + 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) diff --git a/lua/pending/config.lua b/lua/pending/config.lua index 5997994..b61f44a 100644 --- a/lua/pending/config.lua +++ b/lua/pending/config.lua @@ -9,7 +9,7 @@ ---@field date_format string ---@field date_syntax string ---@field category_order? string[] ----@field drawer_height integer +---@field drawer_height? integer ---@field gcal? pending.GcalConfig ---@class pending.config @@ -23,7 +23,6 @@ local defaults = { date_format = '%b %d', date_syntax = 'due', category_order = {}, - drawer_height = 15, } ---@type pending.Config?