local M = {} local function vault_root() local file = vim.api.nvim_buf_get_name(0) local dir = file ~= "" and vim.fn.fnamemodify(file, ":p:h") or vim.fn.getcwd() local probe = dir while probe ~= "/" and probe ~= "" do if vim.loop.fs_stat(probe .. "/time.csv") or vim.loop.fs_stat(probe .. "/.obsidian") then return probe end local parent = vim.fn.fnamemodify(probe, ":h") if parent == probe then break end probe = parent end return vim.fn.getcwd() end local function relpath(abs, base) local a = vim.fn.fnamemodify(abs, ":p") local b = vim.fn.fnamemodify(base, ":p"):gsub("/$", "") if a:sub(1, #b + 1) == b .. "/" then return a:sub(#b + 2) end return a end local function run(cmd, cwd, on_done) local stdout, stderr = {}, {} vim.fn.jobstart(cmd, { cwd = cwd, stdout_buffered = true, stderr_buffered = true, on_stdout = function(_, d) for _, l in ipairs(d) do if l ~= "" then stdout[#stdout + 1] = l end end end, on_stderr = function(_, d) for _, l in ipairs(d) do if l ~= "" then stderr[#stderr + 1] = l end end end, on_exit = function(_, code) vim.schedule(function() if on_done then on_done(stdout, stderr, code) end end) end, }) end local function notify(lines, level) if #lines == 0 then return end vim.notify(table.concat(lines, "\n"), level or vim.log.levels.INFO) end local function extract_desc(line) return (line:gsub("^%s*%-%s*%[.%]%s*", ""):gsub("%s*%^%S+%s*$", ""):gsub("%s+$", "")) end local function ensure_tid_on_line() local line = vim.api.nvim_get_current_line() local tid = line:match("%^(tid%-%S+)") if tid then return tid end if not line:match("^%s*%-%s*%[.%]") then vim.notify("td: not on a todo line (- [ ] ...)", vim.log.levels.WARN) return nil end tid = os.date("tid-%Y%m%d-%H%M%S") local new = line:gsub("%s+$", "") .. " ^" .. tid vim.api.nvim_set_current_line(new) vim.cmd("silent! write") return tid end function M.start() local tid = ensure_tid_on_line() if not tid then return end local cwd = vault_root() local file = relpath(vim.api.nvim_buf_get_name(0), cwd) local desc = extract_desc(vim.api.nvim_get_current_line()) run({ "td", "start", tid, "--file", file, "--desc", desc }, cwd, function(out, err) notify(out); notify(err, vim.log.levels.WARN) end) end function M.stop() run({ "td", "stop" }, vault_root(), function(out, err) notify(out); notify(err, vim.log.levels.WARN) end) end function M.report() run({ "td", "report" }, vault_root(), function(out, err) if #out == 0 then notify(err, vim.log.levels.WARN); return end local buf = vim.api.nvim_create_buf(false, true) vim.api.nvim_buf_set_lines(buf, 0, -1, false, out) vim.api.nvim_buf_set_option(buf, "modifiable", false) local width = math.min(100, vim.o.columns - 4) local height = math.min(20, #out + 2) vim.api.nvim_open_win(buf, true, { relative = "editor", width = width, height = height, row = math.floor((vim.o.lines - height) / 2), col = math.floor((vim.o.columns - width) / 2), style = "minimal", border = "rounded", title = " td report ", title_pos = "center", }) vim.api.nvim_buf_set_keymap(buf, "n", "q", "close", { noremap = true, silent = true }) vim.api.nvim_buf_set_keymap(buf, "n", "", "close", { noremap = true, silent = true }) end) end return M