all repos

init.lua @ b57da0a7dcbca8f1e688a07d7a6b0ce0b32b3dad

my nvim config
3 files changed, 116 insertions(+), 6 deletions(-)
feat(tasks): add agenda view
Author: Olexandr Smirnov olexsmir@gmail.com
Committed at: 2025-08-13 18:32:46 +0300
Parent: 9cdf438
M after/ftplugin/markdown.lua

@@ -17,5 +17,6 @@ chore = { pattern = "chore%:", group = "@character" },

refactor = { pattern = "refactor%:", group = "@comment.info" }, fix = { pattern = "fix%:", group = "@comment.error" }, docs = { pattern = "docs%:", group = "@label" }, + test = { pattern = "test%:", group = "@comment.note" }, }, }
M lua/core/keymaps.lua

@@ -12,6 +12,7 @@

-- notes u.map("n", "<leader>ot", "<cmd>e $HOME/org/todo.txt<cr>") --codespell:ignore u.map("n", "<leader>oi", "<cmd>e $HOME/org/notes/Inbox/Inbox.md<cr>") +u.map("n", "<leader>a", require("scratch.tasks").agenda) -- general u.map("n", "<leader>q", "<cmd>quit!<cr>")
M lua/scratch/tasks.lua

@@ -1,14 +1,42 @@

+---@param name string +---@return string +local function task_file(name) + return vim.fs.joinpath(vim.fn.expand "~/org/notes", name .. ".md") +end + local config = { label = "done:%Y%m%d-%H%M", archive_header = "# Archive", + task_files = { + task_file "TODO", + task_file "Onasty", + task_file "GBAC", + task_file "Projects/gopher.nvim", + }, } --- 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 + +---@param fpath string +---@return string +local function file_name_from_path(fpath) + return fpath:match "^.+/(.+)$" or fpath +end + +---@param short_name string +---@return string? +local function full_file_path_from_short_name(short_name) + for _, fname in ipairs(config.task_files) do + if file_name_from_path(fname) == short_name .. ".md" then + return fname + end + end + return nil +end ---@return string local function get_done_label()

@@ -23,8 +51,29 @@ end

---@param str string ---@return boolean +local function has_next_tag(str) + return str:match "%#next" ~= nil +end + +---@param str string +---@return boolean local function check_task_status(str) return str:match "^(%s*%- )%[x%]" ~= nil +end + +---@param str string +---@return string +local function remove_task_prefix(str) + local res = str:gsub("^%- %[ %] ", "") + return res +end + +---@param fname string +---@param line number +---@return string +local function display_file(fname, line) + local str = "" .. (fname:match "^(.-)%.%w+$" or fname) .. ":" .. line + return (str .. string.rep(" ", 14 - #str)) end -- converts a like with markdown task to completed task, and removes `#next` in it, if there's one

@@ -60,13 +109,72 @@ end

local tasks = {} -function tasks.list_undone() - error "unimplemented" -end +function tasks.agenda() + -- parse all `task_files` for `#next` tag + -- FIXME: that's probably should be cached -function tasks.list_done() - error "unimplemented" + ---@type table<string, {task: string, line: number}[]> + local agenda_tasks = {} + for _, fname in ipairs(config.task_files) do + local lines = vim.fn.readfile(fname) + for i, line in ipairs(lines) do + if is_task(line) and has_next_tag(line) then + local short_name = file_name_from_path(fname) + agenda_tasks[short_name] = agenda_tasks[short_name] or {} + table.insert(agenda_tasks[short_name], { task = line, line = i }) + end + end + end + + -- build the output + local output = { "# Agenda" } + for fname, ftasks in pairs(agenda_tasks) do + for _, ftask in pairs(ftasks) do + table.insert( + output, + display_file(fname, ftask.line) .. " " .. remove_task_prefix(ftask.task) + ) + end + end + + -- open the agenda view + vim.cmd.new() + local buf = vim.api.nvim_get_current_buf() + vim.api.nvim_buf_set_name(buf, "scratch.tasks:agenda") + vim.api.nvim_set_option_value("filetype", "markdown", { buf = buf }) + vim.api.nvim_set_option_value("bufhidden", "wipe", { buf = buf }) + vim.api.nvim_set_option_value("buftype", "nofile", { buf = buf }) + vim.api.nvim_set_option_value("swapfile", false, { buf = buf }) + vim.api.nvim_buf_set_lines(buf, 0, -1, false, output) + vim.api.nvim_set_option_value("modifiable", false, { buf = buf }) + vim.api.nvim_exec_autocmds("FileType", { buffer = buf }) -- loads the ftplugins + vim.api.nvim_win_set_height(0, 10) + vim.api.nvim_win_set_cursor(0, { 2, 0 }) + + -- FIXME: this should be a separate function + vim.keymap.set("n", "<CR>", function() + local line = vim.api.nvim_get_current_line() + local fname, lineno = line:match "^([^:]+):(%d+)" + if fname == nil or lineno == nil then + vim.notify( + "No file name or line number found in the line", + vim.log.levels.WARN + ) + return + end + + local fpath = full_file_path_from_short_name(fname) + if fpath == nil then + vim.notify("No file name found in the line", vim.log.levels.WARN) + return + end + + vim.cmd.edit(fpath) + vim.api.nvim_win_set_cursor(0, { tonumber(lineno), 0 }) + end, { buffer = buf, desc = "Open file under cursor", silent = true }) end + +tasks.agenda() function tasks.complete() vim.cmd.mkview() -- saves current folds/scroll