|
|
@ -7,7 +7,7 @@ local defaults = {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
-- Helper: Parses line details for a given line number (1-based)
|
|
|
|
-- 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
|
|
|
|
-- Returns table: { line = str, indent = str, marker = str|nil, content = str, is_todo = bool, prefix_len = number } or nil
|
|
|
|
local function get_line_details(lnum)
|
|
|
|
local function get_line_details(lnum)
|
|
|
|
-- Use nvim_buf_get_lines which is 0-indexed for ranges
|
|
|
|
-- Use nvim_buf_get_lines which is 0-indexed for ranges
|
|
|
|
local lines = vim.api.nvim_buf_get_lines(0, lnum - 1, lnum, false)
|
|
|
|
local lines = vim.api.nvim_buf_get_lines(0, lnum - 1, lnum, false)
|
|
|
@ -22,19 +22,25 @@ local function get_line_details(lnum)
|
|
|
|
-- Matches lines like: "- [ ] task", "* [x] task", "+ [ ] task"
|
|
|
|
-- Matches lines like: "- [ ] task", "* [x] task", "+ [ ] task"
|
|
|
|
local todo_pattern = "^(%s*[%-%*%+]%s+)%[([ x])%]%s*" -- Capture prefix before checkbox
|
|
|
|
local todo_pattern = "^(%s*[%-%*%+]%s+)%[([ x])%]%s*" -- Capture prefix before checkbox
|
|
|
|
|
|
|
|
|
|
|
|
local _, marker_match, _ = string.match(line, marker_pattern)
|
|
|
|
local prefix_match, marker_match, space_match = string.match(line, marker_pattern)
|
|
|
|
local is_todo = (string.match(line, todo_pattern) ~= nil)
|
|
|
|
local is_todo = (string.match(line, todo_pattern) ~= nil)
|
|
|
|
local content = ""
|
|
|
|
local content = ""
|
|
|
|
|
|
|
|
local prefix_len = 0 -- Length of the string before the content starts
|
|
|
|
|
|
|
|
|
|
|
|
if is_todo then
|
|
|
|
if is_todo then
|
|
|
|
-- Content is everything after "[x] " or "[ ] " (including leading space)
|
|
|
|
-- Content is everything after "[x] " or "[ ] " (including leading space)
|
|
|
|
|
|
|
|
local todo_prefix = string.match(line, todo_pattern) -- The part like "- [ ] "
|
|
|
|
content = string.match(line, todo_pattern .. "(.*)") or ""
|
|
|
|
content = string.match(line, todo_pattern .. "(.*)") or ""
|
|
|
|
|
|
|
|
prefix_len = #todo_prefix
|
|
|
|
elseif marker_match then
|
|
|
|
elseif marker_match then
|
|
|
|
-- Content is everything after "- " / "* " / "+ "
|
|
|
|
-- Content is everything after "- " / "* " / "+ "
|
|
|
|
|
|
|
|
local list_prefix = prefix_match .. marker_match .. space_match -- The part like "- "
|
|
|
|
content = string.match(line, marker_pattern .. "(.*)") or ""
|
|
|
|
content = string.match(line, marker_pattern .. "(.*)") or ""
|
|
|
|
|
|
|
|
prefix_len = #list_prefix
|
|
|
|
else
|
|
|
|
else
|
|
|
|
-- No list marker, just the trimmed line content
|
|
|
|
-- No list marker, just the trimmed line content
|
|
|
|
content = vim.trim(line)
|
|
|
|
content = vim.trim(line)
|
|
|
|
|
|
|
|
prefix_len = #indent -- Length before the trimmed content
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
return {
|
|
|
@ -43,19 +49,21 @@ local function get_line_details(lnum)
|
|
|
|
marker = marker_match, -- '-', '*', '+', or nil
|
|
|
|
marker = marker_match, -- '-', '*', '+', or nil
|
|
|
|
content = content,
|
|
|
|
content = content,
|
|
|
|
is_todo = is_todo,
|
|
|
|
is_todo = is_todo,
|
|
|
|
|
|
|
|
prefix_len = prefix_len, -- Store the calculated prefix length
|
|
|
|
}
|
|
|
|
}
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
-- Helper: Gets details for the current line
|
|
|
|
-- Helper: Gets details for the current line including cursor position
|
|
|
|
local function get_current_line_details()
|
|
|
|
local function get_current_line_details_with_cursor()
|
|
|
|
local cursor_pos = vim.api.nvim_win_get_cursor(0)
|
|
|
|
local cursor_pos = vim.api.nvim_win_get_cursor(0)
|
|
|
|
local lnum = cursor_pos[1] -- 1-based line number
|
|
|
|
local lnum = cursor_pos[1] -- 1-based line number
|
|
|
|
return get_line_details(lnum), lnum
|
|
|
|
local col = cursor_pos[2] -- 0-based byte column
|
|
|
|
|
|
|
|
return get_line_details(lnum), lnum, col
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
-- <leader>ti: Convert line to TODO, cursor after "] " (Normal mode)
|
|
|
|
-- <leader>ti: Convert line to TODO, cursor after "] " (Normal mode)
|
|
|
|
local function add_todo_insert_mode()
|
|
|
|
local function add_todo_insert_mode()
|
|
|
|
local details, lnum = get_current_line_details()
|
|
|
|
local details, lnum = get_current_line_details_with_cursor() -- Use basic helper is fine
|
|
|
|
-- Don't convert if already a TODO or if getting details failed
|
|
|
|
-- Don't convert if already a TODO or if getting details failed
|
|
|
|
if not details or details.is_todo then return end
|
|
|
|
if not details or details.is_todo then return end
|
|
|
|
|
|
|
|
|
|
|
@ -74,7 +82,7 @@ end
|
|
|
|
|
|
|
|
|
|
|
|
-- <leader>ta: Convert line to TODO, cursor at end of line (Normal mode)
|
|
|
|
-- <leader>ta: Convert line to TODO, cursor at end of line (Normal mode)
|
|
|
|
local function add_todo_append_mode()
|
|
|
|
local function add_todo_append_mode()
|
|
|
|
local details, lnum = get_current_line_details()
|
|
|
|
local details, lnum = get_current_line_details_with_cursor() -- Use basic helper is fine
|
|
|
|
if not details or details.is_todo then return end
|
|
|
|
if not details or details.is_todo then return end
|
|
|
|
|
|
|
|
|
|
|
|
local use_marker = details.marker or "-"
|
|
|
|
local use_marker = details.marker or "-"
|
|
|
@ -92,7 +100,7 @@ end
|
|
|
|
|
|
|
|
|
|
|
|
-- <leader>to: Insert new TODO line below, cursor after "] " (Normal mode)
|
|
|
|
-- <leader>to: Insert new TODO line below, cursor after "] " (Normal mode)
|
|
|
|
local function add_todo_new_line_mode()
|
|
|
|
local function add_todo_new_line_mode()
|
|
|
|
local details, lnum = get_current_line_details()
|
|
|
|
local details, lnum = get_current_line_details_with_cursor() -- Use basic helper is fine
|
|
|
|
if not details then return end -- Should not happen usually, but check anyway
|
|
|
|
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
|
|
|
|
-- Determine indent and marker based on current line, default to '-' if no marker
|
|
|
@ -109,11 +117,50 @@ local function add_todo_new_line_mode()
|
|
|
|
vim.api.nvim_win_set_cursor(0, { lnum + 1, cursor_col_bytes })
|
|
|
|
vim.api.nvim_win_set_cursor(0, { lnum + 1, cursor_col_bytes })
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-- <leader>td: Remove TODO markup from the current line (Normal mode)
|
|
|
|
|
|
|
|
local function remove_todo_markup()
|
|
|
|
|
|
|
|
local details, lnum, original_col = get_current_line_details_with_cursor()
|
|
|
|
|
|
|
|
if not details or not details.is_todo then return end -- Only act on TODO lines
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-- Construct the new line without the TODO markup "[ ] " or "[x] "
|
|
|
|
|
|
|
|
local new_line = details.indent .. details.marker .. " " .. details.content
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-- Calculate the position where the markup started
|
|
|
|
|
|
|
|
-- This is the length of the indent + marker + one space
|
|
|
|
|
|
|
|
local markup_start_col = #(details.indent .. details.marker .. " ")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-- The markup itself is 4 characters: '[', ' ' or 'x', ']', ' '
|
|
|
|
|
|
|
|
local markup_len = 4
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-- Update the line content
|
|
|
|
|
|
|
|
vim.api.nvim_buf_set_lines(0, lnum - 1, lnum, false, { new_line })
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-- Calculate the new cursor column
|
|
|
|
|
|
|
|
local new_col
|
|
|
|
|
|
|
|
if original_col < markup_start_col then
|
|
|
|
|
|
|
|
-- Cursor was before the markup, keep its position
|
|
|
|
|
|
|
|
new_col = original_col
|
|
|
|
|
|
|
|
elseif original_col >= markup_start_col and original_col < markup_start_col + markup_len then
|
|
|
|
|
|
|
|
-- Cursor was inside the markup, move it to the start of where the markup was
|
|
|
|
|
|
|
|
new_col = markup_start_col
|
|
|
|
|
|
|
|
else
|
|
|
|
|
|
|
|
-- Cursor was after the markup, shift it left by the length of the removed markup
|
|
|
|
|
|
|
|
new_col = original_col - markup_len
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-- Ensure the column is not negative (shouldn't happen here, but good practice)
|
|
|
|
|
|
|
|
new_col = math.max(0, new_col)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-- Set the new cursor position
|
|
|
|
|
|
|
|
vim.api.nvim_win_set_cursor(0, { lnum, new_col })
|
|
|
|
|
|
|
|
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()
|
|
|
|
-- Handles Enter key press in Insert mode
|
|
|
|
-- Handles Enter key press in Insert mode
|
|
|
|
local function press_enter()
|
|
|
|
local function press_enter()
|
|
|
|
local details, lnum = get_current_line_details()
|
|
|
|
local details, lnum = get_current_line_details_with_cursor() -- Use basic helper is fine
|
|
|
|
if not details then
|
|
|
|
if not details then
|
|
|
|
-- Getting details failed, do nothing and let Neovim handle Enter.
|
|
|
|
-- Getting details failed, do nothing and let Neovim handle Enter.
|
|
|
|
return
|
|
|
|
return
|
|
|
@ -259,6 +306,9 @@ local function setup_buffer_keymaps()
|
|
|
|
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>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>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 })
|
|
|
|
vim.keymap.set("n", "<leader>to", add_todo_new_line_mode, { desc = "Todoer: Add new TODO line below", buffer = 0, silent = true })
|
|
|
|
|
|
|
|
-- Mapping for removing TODO markup
|
|
|
|
|
|
|
|
vim.keymap.set("n", "<leader>td", remove_todo_markup, { desc = "Todoer: Remove TODO markup", 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)
|
|
|
|