all repos

init.lua @ 9cdf438

my nvim config

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