feat: rename
This commit is contained in:
parent
c69a3957c8
commit
78a275d096
21 changed files with 191 additions and 191 deletions
264
lua/pending/init.lua
Normal file
264
lua/pending/init.lua
Normal file
|
|
@ -0,0 +1,264 @@
|
|||
local buffer = require('pending.buffer')
|
||||
local diff = require('pending.diff')
|
||||
local parse = require('pending.parse')
|
||||
local store = require('pending.store')
|
||||
|
||||
---@class task
|
||||
local M = {}
|
||||
|
||||
---@type pending.Task[]?
|
||||
local _undo_state = nil
|
||||
|
||||
---@return integer bufnr
|
||||
function M.open()
|
||||
local bufnr = buffer.open()
|
||||
M._setup_autocmds(bufnr)
|
||||
M._setup_buf_mappings(bufnr)
|
||||
return bufnr
|
||||
end
|
||||
|
||||
---@param bufnr integer
|
||||
function M._setup_autocmds(bufnr)
|
||||
local group = vim.api.nvim_create_augroup('PendingBuffer', { clear = true })
|
||||
vim.api.nvim_create_autocmd('BufWriteCmd', {
|
||||
group = group,
|
||||
buffer = bufnr,
|
||||
callback = function()
|
||||
M._on_write(bufnr)
|
||||
end,
|
||||
})
|
||||
end
|
||||
|
||||
---@param bufnr integer
|
||||
function M._setup_buf_mappings(bufnr)
|
||||
local opts = { buffer = bufnr, silent = true }
|
||||
vim.keymap.set('n', '<CR>', function()
|
||||
M.toggle_complete()
|
||||
end, opts)
|
||||
vim.keymap.set('n', '<Tab>', function()
|
||||
buffer.toggle_view()
|
||||
end, opts)
|
||||
vim.keymap.set('n', 'g?', function()
|
||||
M.show_help()
|
||||
end, opts)
|
||||
end
|
||||
|
||||
---@param bufnr integer
|
||||
function M._on_write(bufnr)
|
||||
local lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false)
|
||||
_undo_state = store.active_tasks()
|
||||
diff.apply(lines)
|
||||
buffer.render(bufnr)
|
||||
end
|
||||
|
||||
function M.toggle_complete()
|
||||
local bufnr = buffer.bufnr()
|
||||
if not bufnr then
|
||||
return
|
||||
end
|
||||
local row = vim.api.nvim_win_get_cursor(0)[1]
|
||||
local meta = buffer.meta()
|
||||
if not meta[row] or meta[row].type ~= 'task' then
|
||||
return
|
||||
end
|
||||
local id = meta[row].id
|
||||
local task = store.get(id)
|
||||
if not task then
|
||||
return
|
||||
end
|
||||
if task.status == 'done' then
|
||||
store.update(id, { status = 'pending', ['end'] = vim.NIL })
|
||||
else
|
||||
store.update(id, { status = 'done' })
|
||||
end
|
||||
store.save()
|
||||
buffer.render(bufnr)
|
||||
end
|
||||
|
||||
function M.toggle_priority()
|
||||
local bufnr = buffer.bufnr()
|
||||
if not bufnr then
|
||||
return
|
||||
end
|
||||
local row = vim.api.nvim_win_get_cursor(0)[1]
|
||||
local meta = buffer.meta()
|
||||
if not meta[row] or meta[row].type ~= 'task' then
|
||||
return
|
||||
end
|
||||
local id = meta[row].id
|
||||
local task = store.get(id)
|
||||
if not task then
|
||||
return
|
||||
end
|
||||
local new_priority = task.priority == 1 and 0 or 1
|
||||
store.update(id, { priority = new_priority })
|
||||
store.save()
|
||||
buffer.render(bufnr)
|
||||
end
|
||||
|
||||
function M.prompt_date()
|
||||
local bufnr = buffer.bufnr()
|
||||
if not bufnr then
|
||||
return
|
||||
end
|
||||
local row = vim.api.nvim_win_get_cursor(0)[1]
|
||||
local meta = buffer.meta()
|
||||
if not meta[row] or meta[row].type ~= 'task' then
|
||||
return
|
||||
end
|
||||
local id = meta[row].id
|
||||
vim.ui.input({ prompt = 'Due date (YYYY-MM-DD): ' }, function(input)
|
||||
if not input then
|
||||
return
|
||||
end
|
||||
local due = input ~= '' and input or nil
|
||||
if due then
|
||||
if not due:match('^%d%d%d%d%-%d%d%-%d%d$') then
|
||||
vim.notify('Invalid date format. Use YYYY-MM-DD.', vim.log.levels.ERROR)
|
||||
return
|
||||
end
|
||||
end
|
||||
store.update(id, { due = due })
|
||||
store.save()
|
||||
buffer.render(bufnr)
|
||||
end)
|
||||
end
|
||||
|
||||
---@param text string
|
||||
function M.add(text)
|
||||
if not text or text == '' then
|
||||
vim.notify('Usage: :Pending add <description>', vim.log.levels.ERROR)
|
||||
return
|
||||
end
|
||||
store.load()
|
||||
local description, metadata = parse.command_add(text)
|
||||
if not description or description == '' then
|
||||
vim.notify('Pending must have a description.', vim.log.levels.ERROR)
|
||||
return
|
||||
end
|
||||
store.add({
|
||||
description = description,
|
||||
category = metadata.cat,
|
||||
due = metadata.due,
|
||||
})
|
||||
store.save()
|
||||
local bufnr = buffer.bufnr()
|
||||
if bufnr and vim.api.nvim_buf_is_valid(bufnr) then
|
||||
buffer.render(bufnr)
|
||||
end
|
||||
vim.notify('Pending added: ' .. description)
|
||||
end
|
||||
|
||||
function M.sync()
|
||||
local ok, gcal = pcall(require, 'pending.sync.gcal')
|
||||
if not ok then
|
||||
vim.notify('Google Calendar sync module not available.', vim.log.levels.ERROR)
|
||||
return
|
||||
end
|
||||
gcal.sync()
|
||||
end
|
||||
|
||||
---@param days? integer
|
||||
function M.archive(days)
|
||||
days = days or 30
|
||||
local cutoff = os.time() - (days * 86400)
|
||||
local tasks = store.tasks()
|
||||
local archived = 0
|
||||
local kept = {}
|
||||
for _, task in ipairs(tasks) do
|
||||
if (task.status == 'done' or task.status == 'deleted') and task['end'] then
|
||||
local y, mo, d, h, mi, s = task['end']:match('^(%d+)-(%d+)-(%d+)T(%d+):(%d+):(%d+)Z$')
|
||||
if y then
|
||||
local t = os.time({
|
||||
year = tonumber(y),
|
||||
month = tonumber(mo),
|
||||
day = tonumber(d),
|
||||
hour = tonumber(h),
|
||||
min = tonumber(mi),
|
||||
sec = tonumber(s),
|
||||
})
|
||||
if t < cutoff then
|
||||
archived = archived + 1
|
||||
goto skip
|
||||
end
|
||||
end
|
||||
end
|
||||
table.insert(kept, task)
|
||||
::skip::
|
||||
end
|
||||
store.replace_tasks(kept)
|
||||
store.save()
|
||||
vim.notify('Archived ' .. archived .. ' tasks.')
|
||||
local bufnr = buffer.bufnr()
|
||||
if bufnr and vim.api.nvim_buf_is_valid(bufnr) then
|
||||
buffer.render(bufnr)
|
||||
end
|
||||
end
|
||||
|
||||
function M.show_help()
|
||||
local cfg = require('pending.config').get()
|
||||
local dk = cfg.date_syntax or 'due'
|
||||
local lines = {
|
||||
'pending.nvim keybindings',
|
||||
'',
|
||||
'<CR> Toggle complete/uncomplete',
|
||||
'<Tab> Switch category/priority view',
|
||||
'o / O Add new task line',
|
||||
'dd Delete task (on :w)',
|
||||
'p / P Paste (duplicates get new IDs)',
|
||||
':w Save all changes',
|
||||
'',
|
||||
':Pending add <text> Quick-add task',
|
||||
':Pending add Cat: <text> Quick-add with category',
|
||||
':Pending sync Push to Google Calendar',
|
||||
':Pending archive [days] Purge old done tasks',
|
||||
'',
|
||||
'Inline metadata (on new lines before :w):',
|
||||
' ' .. dk .. ':YYYY-MM-DD Set due date',
|
||||
' cat:Name Set category',
|
||||
'',
|
||||
'Press q or <Esc> to close',
|
||||
}
|
||||
local buf = vim.api.nvim_create_buf(false, true)
|
||||
vim.api.nvim_buf_set_lines(buf, 0, -1, false, lines)
|
||||
vim.bo[buf].modifiable = false
|
||||
vim.bo[buf].bufhidden = 'wipe'
|
||||
local width = 50
|
||||
local height = #lines
|
||||
local win = vim.api.nvim_open_win(buf, true, {
|
||||
relative = 'editor',
|
||||
width = width,
|
||||
height = height,
|
||||
col = math.floor((vim.o.columns - width) / 2),
|
||||
row = math.floor((vim.o.lines - height) / 2),
|
||||
style = 'minimal',
|
||||
border = 'rounded',
|
||||
})
|
||||
vim.keymap.set('n', 'q', function()
|
||||
vim.api.nvim_win_close(win, true)
|
||||
end, { buffer = buf, silent = true })
|
||||
vim.keymap.set('n', '<Esc>', function()
|
||||
vim.api.nvim_win_close(win, true)
|
||||
end, { buffer = buf, silent = true })
|
||||
end
|
||||
|
||||
---@param args string
|
||||
function M.command(args)
|
||||
if not args or args == '' then
|
||||
M.open()
|
||||
return
|
||||
end
|
||||
local cmd, rest = args:match('^(%S+)%s*(.*)')
|
||||
if cmd == 'add' then
|
||||
M.add(rest)
|
||||
elseif cmd == 'sync' then
|
||||
M.sync()
|
||||
elseif cmd == 'archive' then
|
||||
local d = rest ~= '' and tonumber(rest) or nil
|
||||
M.archive(d)
|
||||
else
|
||||
vim.notify('Unknown Pending subcommand: ' .. cmd, vim.log.levels.ERROR)
|
||||
end
|
||||
end
|
||||
|
||||
return M
|
||||
Loading…
Add table
Add a link
Reference in a new issue