feat: add <leader>ti/ta/to mappings for TODO creation

main
borja (aider) 2 months ago
parent af2c61fa3f
commit 7df445bd74

@ -6,137 +6,193 @@ local defaults = {
filetypes = { "markdown", "text", "norg" }, -- Filetypes to activate the plugin for filetypes = { "markdown", "text", "norg" }, -- Filetypes to activate the plugin for
} }
-- Helper: Parses line details for a given line number (1-based)
-- Returns table: { line = str, indent = str, marker = str|nil, content = str, is_todo = bool } or nil
local function get_line_details(lnum)
-- Use nvim_buf_get_lines which is 0-indexed for ranges
local lines = vim.api.nvim_buf_get_lines(0, lnum - 1, lnum, false)
if not lines or #lines == 0 then return nil end -- Handle potential errors getting line
local line = lines[1]
local indent = string.match(line, "^%s*") or ""
-- Pattern captures marker '-', '*', or '+' followed by at least one space
-- Returns: indent, marker, space_after_marker
local marker_pattern = "^(%s*)([%-%*%+])(%s+)"
-- Pattern captures marker, checkbox '[ ]' or '[x]', and optional space after checkbox
local todo_pattern = "^%s*[%-%*%+]%s+%[([ x])%]%s*"
local _, marker_match, _ = string.match(line, marker_pattern)
local is_todo = (string.match(line, todo_pattern) ~= nil)
local content = ""
if is_todo then
-- Content is everything after "[x] " or "[ ] " (including leading space)
content = string.match(line, todo_pattern .. "(.*)") or ""
elseif marker_match then
-- Content is everything after "- " / "* " / "+ "
content = string.match(line, marker_pattern .. "(.*)") or ""
else
-- No list marker, just the trimmed line content
content = vim.trim(line)
end
return {
line = line,
indent = indent,
marker = marker_match, -- '-', '*', '+', or nil
content = content,
is_todo = is_todo,
}
end
-- Helper: Gets details for the current line
local function get_current_line_details()
local cursor_pos = vim.api.nvim_win_get_cursor(0)
local lnum = cursor_pos[1] -- 1-based line number
return get_line_details(lnum), lnum
end
-- <leader>ti: Convert line to TODO, cursor after "] " (Normal mode)
local function add_todo_insert_mode()
local details, lnum = get_current_line_details()
-- Don't convert if already a TODO or if getting details failed
if not details or details.is_todo then return end
local use_marker = details.marker or "-" -- Default to '-' if no list marker found
local text_content = details.content -- Content already extracted correctly by get_line_details
local new_line = details.indent .. use_marker .. " [ ] " .. text_content
-- Replace current line (lnum is 1-based, set_lines is 0-based)
vim.api.nvim_buf_set_lines(0, lnum - 1, lnum, false, { new_line })
-- Set cursor position: 1-based row, 0-based byte column
-- Position cursor right after the "] "
local cursor_col_bytes = vim.fn.strbytes(details.indent .. use_marker .. " [ ] ")
vim.api.nvim_win_set_cursor(0, { lnum, cursor_col_bytes })
end
-- <leader>ta: Convert line to TODO, cursor at end of line (Normal mode)
local function add_todo_append_mode()
local details, lnum = get_current_line_details()
if not details or details.is_todo then return end
local use_marker = details.marker or "-"
local text_content = details.content
local new_line = details.indent .. use_marker .. " [ ] " .. text_content
vim.api.nvim_buf_set_lines(0, lnum - 1, lnum, false, { new_line })
-- Set cursor position: 1-based row, 0-based byte column
-- Position cursor at the very end of the line content
local cursor_col_bytes = vim.fn.strbytes(new_line)
vim.api.nvim_win_set_cursor(0, { lnum, cursor_col_bytes })
end
-- <leader>to: Insert new TODO line below, cursor after "] " (Normal mode)
local function add_todo_new_line_mode()
local details, lnum = get_current_line_details()
if not details then return end -- Should not happen usually, but check anyway
-- Determine indent and marker based on current line, default to '-' if no marker
local new_indent = details.indent
local new_marker = details.marker or "-"
local new_line_content = new_indent .. new_marker .. " [ ] "
-- Insert after current line (lnum is 1-based, set_lines is 0-based for ranges)
vim.api.nvim_buf_set_lines(0, lnum, lnum, false, { new_line_content })
-- Set cursor position: 1-based row (lnum + 1), 0-based byte column
-- Position cursor at the end of the new line (after "] ")
local cursor_col_bytes = vim.fn.strbytes(new_line_content)
vim.api.nvim_win_set_cursor(0, { lnum + 1, cursor_col_bytes })
end
-- Internal function to set up buffer-local keymaps -- Internal function to set up buffer-local keymaps
local function setup_buffer_keymaps() local function setup_buffer_keymaps()
-- add new todo line when previous is already a todo -- add new todo line when previous is already a todo (Will be refactored in Step 3)
local function press_enter() local function press_enter()
local current_line = vim.api.nvim_get_current_line() local current_line = vim.api.nvim_get_current_line()
local index, spaces = string.find(current_line, "^%s*") -- Check if the current line matches the pattern (Only checks '-' marker currently)
local pattern = "^%s*%- %[[ x]%]%s*$" -- Simplified pattern to check if it's an empty-ish TODO line
-- Check if the current line matches the pattern local content_pattern = "^%s*%- %[[ x]%]%s+(.+)" -- Pattern to check if there's content
local pattern = "^%- %[[ x]%] .*$"
local match = string.match(current_line, pattern) if string.match(current_line, content_pattern) then -- If it's a TODO with content
local indent = string.match(current_line, "^%s*") or ""
if match then vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes("<CR>"..indent.."- [ ] ", true, false, true), "n", false)
if string.len(current_line) >= 7 then elseif string.match(current_line, pattern) then -- If it's an empty TODO line
vim.api.nvim_feedkeys("\n- [ ] ", "n", true) -- Current behavior: clear line and insert newline (will change in Step 3)
else vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes("<Esc>S<CR>", true, false, true), "n", false)
-- go to normal mode before removing the content of the line else -- Not a TODO line
vim.api.nvim_feedkeys(vim.api.nvim_eval('"\\<esc>"'), "n", true) vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes("<CR>", true, false, true), "n", false)
vim.api.nvim_feedkeys("_", "n", true)
vim.api.nvim_feedkeys("C", "n", true)
vim.api.nvim_feedkeys("\n", "n", true)
-- vim.api.nvim_set_current_line("")
end
else
vim.api.nvim_feedkeys("\n", "n", true)
end end
end end
-- indent line if tab is pressed when line is a todo -- indent line if tab is pressed when line is a todo (Will be refactored in Step 4)
local function press_tab() local function press_tab()
local current_line = vim.api.nvim_get_current_line() local current_line = vim.api.nvim_get_current_line()
-- Check if current line matches the patterns (Only checks '-' marker currently)
-- Check if current line matches the patterns local pattern = "^%s*%- %[[ x]%]"
local openpattern = "%- %[[ ]%]" if string.match(current_line, pattern) then
local closedpattern = "%- %[[x]%]" -- print("tab pressed, allegedly") -- Removed print
vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes("<C-t>", true, false, true), "i", false)
if string.match(current_line, openpattern) or string.match(current_line, closedpattern) then else
print("tab pressed, allegedly") -- Return <Tab> to allow default behavior if expr=true is set on mapping
vim.api.nvim_feedkeys("C-t", "i", true) return vim.api.nvim_replace_termcodes("<Tab>", true, false, true)
end end
end end
-- indent line if shift tab is pressed when line is a todo -- indent line if shift tab is pressed when line is a todo (Will be refactored in Step 4)
local function press_shift_tab() local function press_shift_tab()
local current_line = vim.api.nvim_get_current_line() local current_line = vim.api.nvim_get_current_line()
-- Check if current line matches the patterns (Only checks '-' marker currently)
-- Check if current line matches the patterns local pattern = "^%s*%- %[[ x]%]"
local openpattern = "%- %[[ ]%]" if string.match(current_line, pattern) then
local closedpattern = "%- %[[x]%]" -- print("shift tab pressed, allegedly") -- Removed print
vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes("<C-d>", true, false, true), "i", false)
if string.match(current_line, openpattern) or string.match(current_line, closedpattern) then else
print("shift tab pressed, allegedly") -- Return <S-Tab> to allow default behavior if expr=true is set on mapping
vim.api.nvim_feedkeys("C-d", "i", true) return vim.api.nvim_replace_termcodes("<S-Tab>", true, false, true)
end end
end end
-- function that checks if the current line starts with the string "- [ ]" or "- [x]" and toggles the x -- function that checks if the current line starts with the string "- [ ]" or "- [x]" and toggles the x
-- (Will be updated in Step 2 to handle *, +)
local function toggle_todo() local function toggle_todo()
local openpattern = "%- %[[ ]%]" local openpattern = "%- %[[ ]%]"
local closedpattern = "%- %[[x]%]" local closedpattern = "%- %[[x]%]"
local titleopenpattern = "# %[[ ]%]" local titleopenpattern = "# %[[ ]%]" -- Keep title toggle for now? Or remove? Let's keep it.
local titleclosedpattern = "# %[[x]%]" local titleclosedpattern = "# %[[x]%]"
local line = vim.api.nvim_get_current_line() local line = vim.api.nvim_get_current_line()
if string.match(line, openpattern) then if string.match(line, openpattern) then
line = string.gsub(line, openpattern, "- [x]") line = string.gsub(line, openpattern, "- [x]", 1) -- Replace only first instance
vim.api.nvim_set_current_line(line) vim.api.nvim_set_current_line(line)
elseif string.match(line, closedpattern) then elseif string.match(line, closedpattern) then
line = string.gsub(line, closedpattern, "- [ ]") line = string.gsub(line, closedpattern, "- [ ]", 1) -- Replace only first instance
vim.api.nvim_set_current_line(line) vim.api.nvim_set_current_line(line)
elseif string.match(line, titleopenpattern) then elseif string.match(line, titleopenpattern) then
line = string.gsub(line, titleopenpattern, "# [x]") line = string.gsub(line, titleopenpattern, "# [x]", 1)
vim.api.nvim_set_current_line(line) vim.api.nvim_set_current_line(line)
elseif string.match(line, titleclosedpattern) then elseif string.match(line, titleclosedpattern) then
line = string.gsub(line, titleclosedpattern, "# [ ]") line = string.gsub(line, titleclosedpattern, "# [ ]", 1)
vim.api.nvim_set_current_line(line)
end
end
-- function that checks if the current line doesn't start with the string "- [ ] " and, if it doesn't, adds the string at the beginning of the line
local function add_todo()
local openpattern = "^%s*%- %[[ ]%]"
local closedpattern = "^%s*%- %[[x]%]"
local line = vim.api.nvim_get_current_line()
local index, spaces = string.find(line, "^%s*")
if index == 1 and not string.match(line, openpattern) and not string.match(line, closedpattern) then
if index == 1 and spaces == 0 then
line = "- [ ] " .. line
vim.api.nvim_set_current_line(line)
vim.api.nvim_feedkeys("A", "n", true)
elseif index == 1 and spaces > 0 then
local trimedline = string.sub(line, spaces + 1)
local spacestring = ""
for x = 1, spaces, 1 do
spacestring = spacestring .. " "
end
line = spacestring .. "- [ ] " .. trimedline
vim.api.nvim_set_current_line(line)
end
end
end
-- function that checks if the current line starts with the string "- [ ]" or "- [x]" and, if it does, removes that string from the line
local function remove_todo()
local openpattern = "%- %[[ ]%]"
local closedpattern = "%- %[[x]%]"
local line = vim.api.nvim_get_current_line()
-- local index, spaces = string.find(line, "^%s*")
if string.match(line, openpattern) or string.match(line, closedpattern) then
print("encontrado")
line = string.gsub(line, openpattern, "")
line = string.gsub(line, closedpattern, "")
vim.api.nvim_set_current_line(line) vim.api.nvim_set_current_line(line)
end end
end end
-- Set up buffer-local keymaps -- Set up buffer-local keymaps
-- Use buffer = 0 to target the current buffer -- Use buffer = 0 to target the current buffer
local map_opts = { noremap = true, silent = true, buffer = 0 } -- Note: Setting expr = true for Tab/S-Tab allows returning keys for default behavior
local expr_map_opts = { noremap = true, expr = true, silent = true, buffer = 0 } vim.keymap.set("n", "<leader>t", function() end, { desc = "+TODOs", buffer = 0 }) -- Placeholder
vim.keymap.set("i", "<CR>", press_enter, { desc = "Todoer: Handle Enter", expr = true, buffer = 0 })
vim.keymap.set("n", "<leader>t", function() end, { desc = "+TODOs", buffer = 0 }) -- Placeholder, keep silent=false if needed vim.keymap.set("i", "<TAB>", press_tab, { desc = "Todoer: Handle Tab", expr = true, buffer = 0 })
vim.keymap.set("i", "<CR>", press_enter, { desc = "Todoer: Handle Enter", expr = true, buffer = 0 }) -- Keep expr=true, noremap=true is default vim.keymap.set("i", "<S-Tab>", press_shift_tab, { desc = "Todoer: Handle Shift-Tab", expr = true, buffer = 0 })
vim.keymap.set("i", "<TAB>", press_tab, { desc = "Todoer: Handle Tab", expr = true, buffer = 0 }) -- Keep expr=true
vim.keymap.set("i", "<S-Tab>", press_shift_tab, { desc = "Todoer: Handle Shift-Tab", expr = true, buffer = 0 }) -- Keep expr=true
vim.keymap.set("n", "<leader>tt", toggle_todo, { desc = "Todoer: Toggle TODO", buffer = 0 }) vim.keymap.set("n", "<leader>tt", toggle_todo, { desc = "Todoer: Toggle TODO", buffer = 0 })
vim.keymap.set("n", "<leader>ta", add_todo, { desc = "Todoer: Add TODO", buffer = 0 }) -- New mappings for adding TODOs
vim.keymap.set("n", "<leader>td", remove_todo, { desc = "Todoer: Remove TODO", buffer = 0 }) vim.keymap.set("n", "<leader>ti", add_todo_insert_mode, { desc = "Todoer: Add TODO (cursor after marker)", buffer = 0, silent = true })
vim.keymap.set("n", "<leader>ta", add_todo_append_mode, { desc = "Todoer: Add TODO (cursor at end)", buffer = 0, silent = true })
vim.keymap.set("n", "<leader>to", add_todo_new_line_mode, { desc = "Todoer: Add new TODO line below", buffer = 0, silent = true })
-- Optional: Notify that keymaps are set for this buffer -- Optional: Notify that keymaps are set for this buffer
-- vim.notify("Todoer keymaps activated for this buffer", vim.log.levels.INFO) -- vim.notify("Todoer keymaps activated for this buffer", vim.log.levels.INFO)

Loading…
Cancel
Save