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