init.lua/lua/scratch/tasks.lua(view raw)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 |
local config = {
label = "done:%Y%m%d-%H%M",
archive_header = "# Archive",
}
-- TODO: highlight the `feat:`, `docs:`, `fix:`, probably should be done with `mini.hipatterns`
-- TODO: add support for multiple line tasks
-- TODO: undoing tasks, if task is marked as done, and has `done` label, it should replace done with `undone`
-- TODO: sort tasks under `# Tasks`, and move tasks with `#next` tag, on top
-- TODO: show the progress of tasks(if task has subtasks, show in virtual text how many of them is done)
-- sub tasks should be only archived with the parent task
---@return string
local function get_done_label()
return os.date(config.label) --[[@as string]]
end
---@param str string
---@return boolean
local function is_task(str)
return str:match "^%s*%- %[[x ]%]" ~= nil
end
---@param str string
---@return boolean
local function check_task_status(str)
return str:match "^(%s*%- )%[x%]" ~= nil
end
-- converts a like with markdown task to completed task, and removes `#next` in it, if there's one
---@param str string
---@return string?
local function to_complete_task(str)
local label = get_done_label()
local task_prefix = str:match "^(%s*%- %[[x ]%])"
if not task_prefix then
return nil
end
str = task_prefix .. " `" .. label .. "`" .. str:sub(#task_prefix + 1)
str = str:gsub("^(%s*%- )%[%s*%]", "%1[x]")
str = str:gsub("%#next", "")
str = str:gsub("%s+$", "")
return str
end
---@param lines string[]
---@return number? - Line of the heading, nil if not found
local function find_archive_heading(lines)
local heading_line = nil
for i, line in ipairs(lines) do
if line:match("^%s*" .. config.archive_header) then
heading_line = i
break
end
end
return heading_line
end
local tasks = {}
function tasks.list_undone()
error "unimplemented"
end
function tasks.list_done()
error "unimplemented"
end
function tasks.complete()
vim.cmd.mkview() -- saves current folds/scroll
local bufnr = vim.api.nvim_get_current_buf()
local cur_pos = vim.api.nvim_win_get_cursor(0)
local lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false)
local task_index = cur_pos[1]
-- if cursor is beyond last line, exit
if task_index > #lines then
vim.cmd.loadview()
return
end
if not is_task(lines[task_index]) then
vim.notify("Not a task", vim.log.levels.WARN)
vim.cmd.loadview()
return
end
if check_task_status(lines[task_index]) then
vim.notify("Task already completed", vim.log.levels.ERROR)
vim.cmd.loadview()
return
end
local archived_heading = find_archive_heading(lines)
if archived_heading == nil then
table.insert(lines, "")
table.insert(lines, config.archive_header)
vim.api.nvim_buf_set_lines(bufnr, 0, -1, true, lines)
archived_heading = #lines
end
local completed_task = to_complete_task(lines[task_index])
table.remove(lines, task_index)
vim.api.nvim_buf_set_lines(bufnr, 0, -1, true, lines)
table.insert(lines, archived_heading, completed_task)
vim.api.nvim_buf_set_lines(bufnr, 0, -1, true, lines)
vim.cmd "silent update"
vim.cmd.loadview()
end
return tasks
|