my neovim config, who would've thought

tasks: quickfix, #n as #next, keymaps

- implement custom formatting for qf
- threat #n as #next
- some small refactoring
- add keymaps for new task, and tagging as #next
- add keymaps to open todo and inbox files

olexsmir.xyz 7a6a728e ebdcb5d9

verified
Changed files
+52 -22
after
ftplugin
lua
core
scratch
+3 -1
after/ftplugin/markdown.lua
··· 8 8 map("n", "<localleader>v", "<cmd>RenderMarkdown toggle<cr>", true) 9 9 map("n", "<localleader>t", require("scratch.tasks").complete, true) 10 10 map("n", "<localleader>c", require("scratch.tasks").clear_archive, true) 11 + map("n", "<localleader>a", "<cmd>norm o- [ ] <cr>a", true) 12 + map("n", "<localleader>n", "<cmd>norm A #n<cr>", true) 11 13 12 14 vim.b.minihipatterns_config = { 13 15 highlighters = { 14 - next = { pattern = "%#next", group = "MiniTestPass" }, 16 + next = { pattern = "%#n[ext]*", group = "MiniTestPass" }, 15 17 front = { pattern = "front%:", group = "@boolean" }, 16 18 feat = { pattern = "feat%:", group = "@boolean" }, 17 19 chore = { pattern = "chore%:", group = "@character" },
+2
lua/core/keymaps.lua
··· 10 10 11 11 -- notes 12 12 u.map("n", "<leader>a", require("scratch.tasks").agenda) 13 + u.map("n", "<leader>nt", "<cmd>e ~/org/notes/TODO.md<cr>") 14 + u.map("n", "<leader>nn", "<cmd>e ~/org/notes/0Inbox.md<cr>") 13 15 14 16 -- general 15 17 u.map({ "n", "x" }, "<leader>z", "1z=")
+47 -21
lua/scratch/tasks.lua
··· 4 4 tasks_file = vim.fn.stdpath "config" .. "/tasks.json", 5 5 } 6 6 7 - -- TODO: multi line tasks support 8 - -- TODO: add support for nested tasks(one level max) 9 - -- completing a nested task will tick it, not archive 10 - -- once the parent task get archived, only then it's "children" gets archived 11 - -- TODO: show progress of task with nested tasks(visual text, with, e.g 5/9) 7 + local ns = vim.api.nvim_create_namespace "scratch.tasks" 12 8 13 9 ---@return string[] 14 10 local function get_tasks_files() ··· 19 15 return vim.json.decode(f:read "*a")["files"] or error "'files' is not found" 20 16 end 21 17 18 + local function display_filename(str) 19 + local res = vim.fs.basename(str) 20 + res = res:gsub("%.%w+$", "") 21 + return res 22 + end 23 + 22 24 ---@param str string 23 25 local function is_task(str) 24 26 return str:match "^%s*%- %[[x ]%]" ~= nil ··· 31 33 32 34 ---@param str string 33 35 local function has_next_tag(str) 34 - return str:match "%#next" ~= nil 36 + return str:match "%#n[ext]*" ~= nil 35 37 end 36 38 37 39 ---@param str string ··· 40 42 end 41 43 42 44 ---@param str string 43 - local function remove_task_prefix(str) 44 - local res = str:gsub("^%- %[ %] ", "") 45 - return res 46 - end 47 - 48 - ---@param str string 49 - local function remove_note_link(str) 45 + local function remove_task_link(str) 50 46 local res = str:gsub("%[%[(.-)%]%]", "[%1]") 51 47 return res 52 48 end 53 49 54 50 ---@param str string 55 51 local function remove_next_tag(str) 56 - local res = str:gsub(" %#next", "") 52 + local res = str:gsub(" %#n[ext]*", "") 57 53 return res 58 54 end 59 55 ··· 66 62 67 63 local label = os.date(config.label) --[[@as string]] 68 64 str = task_prefix .. " `" .. label .. "`" .. str:sub(#task_prefix + 1) 69 - str = str:gsub("^(%s*%- )%[%s*%]", "%1[x]") 70 - str = remove_note_link(str) 65 + str = str:gsub("^(%s*%- )%[%s*%]", "%1[x]") -- mark task as complete 66 + str = remove_task_link(str) 71 67 str = remove_next_tag(str) 72 - str = str:gsub("%s+$", "") 68 + str = str:gsub("%s+$", "") -- white space in the end 73 69 return str 74 70 end 75 71 ··· 83 79 84 80 local tasks = {} 85 81 function tasks.agenda() 86 - local qf_output = vim 82 + local qf_items = vim 87 83 .iter(get_tasks_files()) 88 84 :map(function(fname) 89 85 return vim ··· 93 89 end) 94 90 :map(function(lnum, line) 95 91 local task = remove_next_tag(line) 96 - task = remove_task_prefix(task) 97 - task = remove_note_link(task) 92 + task = remove_task_link(task) 93 + task = task:gsub("^%- %[ %] ", "") -- remove task prefix 94 + task = task:gsub("`", "") 98 95 99 96 return { 100 97 lnum = lnum, 101 98 filename = fname, 102 99 text = task, 100 + user_data = { filename = fname }, 103 101 } --[[@as vim.quickfix.entry]] 104 102 end) 105 103 :totable() ··· 107 105 :flatten() 108 106 :totable() 109 107 110 - vim.fn.setqflist(qf_output, "r") 108 + vim.fn.setqflist({}, "r", { 109 + nr = "$", 110 + title = "scratch.tasks", 111 + items = qf_items, 112 + quickfixtextfunc = function(info) 113 + local items = vim.fn.getqflist({ id = info.id, items = 1 }).items 114 + local lines, highlights = {}, {} 115 + for item = info.start_idx, info.end_idx do 116 + local entry = items[item] 117 + local fname = display_filename(entry.user_data.filename) 118 + local fname_paded = fname .. string.rep(" ", 7 - #fname) .. " " 119 + 120 + table.insert(lines, fname_paded .. entry.text) 121 + table.insert(highlights, { fname_len = #fname_paded }) 122 + end 123 + 124 + vim.schedule(function() 125 + vim.api.nvim_buf_clear_namespace(0, ns, 0, -1) 126 + for i, hl in ipairs(highlights) do 127 + local line = info.start_idx + i - 2 128 + vim.hl.range(0, ns, "qfFileName", { line, 0 }, { line, hl.fname_len }) 129 + vim.hl.range(0, ns, "Bold", { line, hl.fname_len }, { line, -1 }) 130 + end 131 + end) 132 + 133 + return lines 134 + end, 135 + }) 136 + 111 137 vim.cmd.copen() 112 138 end 113 139