nvim: Improve markdown checkbox toggling

Also given new mapping. Hit `<c-t>` instead of `<c-x>`. `<c-x>` (in
insert mode) instead now brings up the path completion which was
previously on the other mapping.

On any line in a markdown-like file (i.e. markdown, quarto, djot, etc.),
we can hit `<c-t>` in normal mode or insert mode to toggle the current
line having a checkbox or not. It takes care to leave the current item a
list item like it was if it already was filled with content.

It does _not_ remove the list item even if it is empty, this may be an
improvement for the future (i.e., empty line -> we hit <c-t> -> line
turns into `- [ ] ` -> we hit <c-t> -> line stays `- `).
But care should be taken to not remove a list item if we don't intend
to, e.g. we could have toggled part of a list beforehand and don't want
to remove the list on each toggle. That's why it is more conservative
for now and I think it should work well enough (the case is likely to be
rare in my mind).

Also, while it does pick up the extended checkbox symbols ([o], [~], [-]),
those are currently hardcoded into the query.
In my mind it should pick those up dynamically from another plugin
instead of hardcoding here, e.g. render-markdown which also defines the
symbols?

Lastly, we could extend it to use treesitter queries instead / on top if
TS is found which would make it more robust than regex matching. But for
an hour of hacking it works quite well.
This commit is contained in:
Marty Oehme 2025-07-17 12:18:38 +02:00
parent 168475a988
commit 083973e7c1
Signed by: Marty
GPG key ID: 4E535BC19C61886E
2 changed files with 20 additions and 10 deletions

View file

@ -11,18 +11,28 @@ if require("core.util").is_available("which-key") then
})
end
-- add tasks w/ <C-x>
map({ "i" }, "<C-x>", function()
local line = vim.api.nvim_get_current_line()
-- Toggles existence of a md checkbox (`- [ ] `) on current line
-- Can be used on list lines, non-list lines or existing checkbox
local function toggle_checkbox()
local cursor = vim.api.nvim_win_get_cursor(0)
-- remove existing prefixes if any
-- TODO: Improved matching for e.g. '- [ ]' already on line, or indented '-'
-- and add task on line below if line is already populated
local updated_line = line:gsub("^%s*[-*]%s*", "", 1)
local line = vim.api.nvim_get_current_line()
local updated_line
-- look for existing checkbox
if line:find("^%s*[-*]%s%[[%sxo]%]") then
updated_line = line:gsub("^(%s*)([-*]?)%s*%[[%sxo-~]%]", "%1%2", 1)
-- look for existing list dash/asterisk
elseif line:find("^%s*[-*]%s") then
updated_line = line:gsub("^(%s*)([-*])%s*", "%1%2 [ ] ", 1)
-- add to non-list line
else
updated_line = line:gsub("^(%s*)", "%1- [ ] ", 1)
end
vim.api.nvim_set_current_line(updated_line)
vim.api.nvim_win_set_cursor(0, { cursor[1], #updated_line })
vim.api.nvim_put({ "- [ ] " }, "c", true, true)
end)
end
-- add tasks w/ <C-x>
map({ "n", "i" }, "<C-t>", toggle_checkbox)
if require("core.util").is_available("zk") and require("zk.util").notebook_root(vim.fn.expand("%:p")) ~= nil then
map("n", "<CR>", "<cmd>lua vim.lsp.buf.definition()<cr>", { silent = true })

View file

@ -191,7 +191,7 @@ return { -- file/item pickers and managers
desc = "path complete",
},
{
"<c-t>",
"<c-x>",
function()
require("fzf-lua").complete_path()
end,