mardown tasks

This commit is contained in:
Thomas Smallwood 2026-03-05 23:56:42 +01:00
parent 978fd1ee21
commit 162c6ceced
3 changed files with 107 additions and 18 deletions

View file

@ -1,7 +1,9 @@
{
"permissions": {
"allow": [
"Bash(grep -rn \"nvim_exec_autocmds\\\\|User.*ClaudeCode\\\\|ClaudeCode.*User\" /home/user/.local/share/nvim/lazy/claudecode.nvim/ --include=\"*.lua\" 2>/dev/null | grep -v test | grep -v spec)"
"Bash(grep -rn \"nvim_exec_autocmds\\\\|User.*ClaudeCode\\\\|ClaudeCode.*User\" /home/user/.local/share/nvim/lazy/claudecode.nvim/ --include=\"*.lua\" 2>/dev/null | grep -v test | grep -v spec)",
"Bash(grep:*)",
"Bash(python3:*)"
]
}
}

View file

@ -1,10 +1,10 @@
{
"CopilotChat.nvim": { "branch": "main", "commit": "743d6005fb412c85309d3f3aa45f18f3a2fb2098" },
"LazyVim": { "branch": "main", "commit": "fca0af57cc3851b14f96a795a9c9bfafc5096dd1" },
"blink-copilot": { "branch": "main", "commit": "7ad8209b2f880a2840c94cdcd80ab4dc511d4f39" },
"blink.cmp": { "branch": "main", "commit": "4b18c32adef2898f95cdef6192cbd5796c1a332d" },
"bufferline.nvim": { "branch": "main", "commit": "655133c3b4c3e5e05ec549b9f8cc2894ac6f51b3" },
"catppuccin": { "branch": "main", "commit": "0a5de4da015a175f416d6ef1eda84661623e0500" },
"claudecode.nvim": { "branch": "main", "commit": "aa9a5cebebdbfa449c1c5ff229ba5d98e66bafed" },
"conform.nvim": { "branch": "master", "commit": "40dcec5555f960b0a04340d76eabdf4efe78599d" },
"copilot.lua": { "branch": "master", "commit": "00446a63cba4cc59bb24fc1e210a555a3e4acdfb" },
"flash.nvim": { "branch": "main", "commit": "fcea7ff883235d9024dc41e638f164a450c14ca2" },
@ -14,7 +14,6 @@
"lazy.nvim": { "branch": "main", "commit": "85c7ff3711b730b4030d03144f6db6375044ae82" },
"lazydev.nvim": { "branch": "main", "commit": "5231c62aa83c2f8dc8e7ba957aa77098cda1257d" },
"lualine.nvim": { "branch": "master", "commit": "47f91c416daef12db467145e16bed5bbfe00add8" },
"markdown-preview.nvim": { "branch": "master", "commit": "a923f5fc5ba36a3b17e289dc35dc17f66d0548ee" },
"mason-lspconfig.nvim": { "branch": "main", "commit": "a324581a3c83fdacdb9804b79de1cbe00ce18550" },
"mason.nvim": { "branch": "main", "commit": "44d1e90e1f66e077268191e3ee9d2ac97cc18e65" },
"mini.ai": { "branch": "main", "commit": "4b0a6207341d895b6cfe9bcb1e4d3e8607bfe4f4" },

View file

@ -25,15 +25,74 @@ local function new_task()
end
local function toggle_task()
local line = vim.api.nvim_get_current_line()
local lines = vim.api.nvim_buf_get_lines(0, 0, -1, false)
local row = vim.api.nvim_win_get_cursor(0)[1]
local line = lines[row]
if not line:find("%[[ x]%]") then return end
-- Toggle current line
local new_state
if line:find("%[ %]") then
vim.api.nvim_set_current_line((line:gsub("%[ %]", "[x]", 1)))
elseif line:find("%[x%]") then
vim.api.nvim_set_current_line((line:gsub("%[x%]", "[ ]", 1)))
lines[row] = line:gsub("%[ %]", "[x]", 1)
new_state = "[x]"
else
return
lines[row] = line:gsub("%[x%]", "[ ]", 1)
new_state = "[ ]"
end
-- stats updated below after toggle
local cur_indent = #(line:match("^(%s*)") or "")
-- Cascade down: set all deeper-indented children to same state
for i = row + 1, #lines do
local l = lines[i]
if not l:match("^%s*%-") then break end
local indent = #(l:match("^(%s*)") or "")
if indent <= cur_indent then break end
if new_state == "[x]" then
lines[i] = l:gsub("%[ %]", "[x]", 1)
else
lines[i] = l:gsub("%[x%]", "[ ]", 1)
end
end
-- Cascade up: find parent (less indent, is a task)
local parent_row
for i = row - 1, 1, -1 do
local l = lines[i]
if not l:match("^%s*%-") then break end
local indent = #(l:match("^(%s*)") or "")
if indent < cur_indent and l:find("%[[ x]%]") then
parent_row = i
break
end
end
if parent_row then
if new_state == "[ ]" and lines[parent_row]:find("%[x%]") then
-- Unchecking child: uncheck parent
lines[parent_row] = lines[parent_row]:gsub("%[x%]", "[ ]", 1)
elseif new_state == "[x]" then
-- Checking child: check parent if all siblings are now done
local parent_indent = #(lines[parent_row]:match("^(%s*)") or "")
local all_done = true
for i = parent_row + 1, #lines do
local l = lines[i]
if not l:match("^%s*%-") then break end
local indent = #(l:match("^(%s*)") or "")
if indent <= parent_indent then break end
if indent == cur_indent and l:find("%[ %]") then
all_done = false
break
end
end
if all_done then
lines[parent_row] = lines[parent_row]:gsub("%[ %]", "[x]", 1)
end
end
end
vim.api.nvim_buf_set_lines(0, 0, -1, false, lines)
vim.api.nvim_win_set_cursor(0, { row, vim.api.nvim_win_get_cursor(0)[2] })
local update = require("config.markdown-tasks")._update_stats
if update then update() end
end
@ -62,9 +121,22 @@ end
-- ── Section task stats ────────────────────────────────────────────────────────
-- Parses a "DD-MM-YYYY" string into a Unix timestamp for comparison.
local function date_to_time(s)
local d, m, y = s:match("(%d+)-(%d+)-(%d+)")
if not d then return 0 end
return os.time({ year = tonumber(y), month = tonumber(m), day = tonumber(d) })
end
-- Strips the stats suffix from a heading line, returning the bare heading.
local function strip_stats(line)
return (line:gsub("%s+✅%d+/%d+%s*$", ""))
return (line
:gsub("%s+✅ ?%d+/%d+ ?✅%s*$", "")
:gsub("%s+❌ ?%d+/%d+ ?❌%s*$", "")
:gsub("%s+📜 ?%d+/%d+ ?📜%s*$", "")
:gsub("%s+⏳ ?%d+/%d+ ?⏳%s*$", "")
:gsub("%s+📅 ?%d+/%d+ ?📅%s*$", "")
:gsub("%s+🚨 ?%d+/%d+ ?🚨%s*$", ""))
end
-- Counts [x] and [ ] tasks in a section starting at header_line (1-indexed).
@ -81,6 +153,19 @@ local function count_section_tasks(lines, header_line)
return done, todo
end
local function classify_section(base)
local date_str = base:match("^## (%d%d%-%d%d%-%d%d%d%d)$")
if not date_str then return "general" end
local section_time = date_to_time(date_str)
local today_start = date_to_time(os.date("%d-%m-%Y"))
if section_time == today_start then return "today" end
return section_time > today_start and "future" or "past"
end
local section_icons = {
general = "📜", today = "", future = "📅", past = "",
}
-- Updates every ## heading with a live task count suffix.
local function update_section_stats()
local lines = vim.api.nvim_buf_get_lines(0, 0, -1, false)
@ -89,7 +174,17 @@ local function update_section_stats()
if line:match("^## ") then
local base = strip_stats(line)
local done, todo = count_section_tasks(lines, i)
local new_line = base .. "" .. done .. "/" .. todo
local total = done + todo
local kind = classify_section(base)
local icon
if total > 0 and done == total then
icon = ""
elseif kind == "today" and done == 0 and total >= 2 then
icon = "🚨"
else
icon = section_icons[kind]
end
local new_line = base .. " " .. icon .. " " .. done .. "/" .. total .. " " .. icon
if new_line ~= line then
lines[i] = new_line
changed = true
@ -166,13 +261,6 @@ local function section_end(header_line)
return last
end
-- Parses a "DD-MM-YYYY" string into a Unix timestamp for comparison.
local function date_to_time(s)
local d, m, y = s:match("(%d+)-(%d+)-(%d+)")
if not d then return 0 end
return os.time({ year = tonumber(y), month = tonumber(m), day = tonumber(d) })
end
-- Inserts a new "## date_str" line in descending date order (newest first).
-- Blank lines are left to normalize_spacing.
local function insert_date_section(date_str)