|
|
@ -9,50 +9,67 @@ 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, prefix_len = number } 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)
|
|
|
|
if not lines or #lines == 0 then return nil end -- Handle potential errors getting line
|
|
|
|
if not lines or #lines == 0 then return nil end -- Handle potential errors getting line
|
|
|
|
local line = lines[1]
|
|
|
|
local line = lines[1]
|
|
|
|
|
|
|
|
|
|
|
|
local indent = string.match(line, "^%s*") or ""
|
|
|
|
-- Define patterns
|
|
|
|
-- Pattern captures marker '-', '*', or '+' followed by at least one space
|
|
|
|
-- Pattern to capture the full TODO prefix, including trailing space(s)
|
|
|
|
-- Returns: indent, marker, space_after_marker
|
|
|
|
-- Captures: 1: Full prefix, 2: Indent, 3: Marker, 4: Checkbox state
|
|
|
|
local marker_pattern = "^(%s*)([%-%*%+])(%s+)"
|
|
|
|
local todo_prefix_pattern = "^(%s*([%-%*%+])%s+%[([ x])%]%s*)"
|
|
|
|
-- Pattern captures marker, checkbox '[ ]' or '[x]', and optional space after checkbox
|
|
|
|
-- Pattern to capture a regular list prefix
|
|
|
|
-- Matches lines like: "- [ ] task", "* [x] task", "+ [ ] task"
|
|
|
|
-- Captures: 1: Full prefix, 2: Indent, 3: Marker
|
|
|
|
local todo_pattern = "^(%s*[%-%*%+]%s+)%[([ x])%]%s*" -- Capture prefix before checkbox
|
|
|
|
local list_prefix_pattern = "^(%s*([%-%*%+])%s+)"
|
|
|
|
|
|
|
|
|
|
|
|
local prefix_match, marker_match, space_match = string.match(line, marker_pattern)
|
|
|
|
local indent = ""
|
|
|
|
local is_todo = (string.match(line, todo_pattern) ~= nil)
|
|
|
|
local marker = nil
|
|
|
|
local content = ""
|
|
|
|
local content = ""
|
|
|
|
local prefix_len = 0 -- Length of the string before the content starts
|
|
|
|
local is_todo = false
|
|
|
|
|
|
|
|
local prefix_len = 0
|
|
|
|
if is_todo then
|
|
|
|
|
|
|
|
-- Content is everything after "[x] " or "[ ] " (including leading space)
|
|
|
|
-- Try matching the TODO prefix first
|
|
|
|
local todo_prefix = string.match(line, todo_pattern) -- The part like "- [ ] "
|
|
|
|
local todo_prefix, todo_indent, todo_marker, _ = string.match(line, todo_prefix_pattern)
|
|
|
|
content = string.match(line, todo_pattern .. "(.*)") or ""
|
|
|
|
|
|
|
|
prefix_len = #todo_prefix
|
|
|
|
if todo_prefix then
|
|
|
|
elseif marker_match then
|
|
|
|
-- It's a TODO line
|
|
|
|
-- Content is everything after "- " / "* " / "+ "
|
|
|
|
is_todo = true
|
|
|
|
local list_prefix = prefix_match .. marker_match .. space_match -- The part like "- "
|
|
|
|
indent = todo_indent
|
|
|
|
content = string.match(line, marker_pattern .. "(.*)") or ""
|
|
|
|
marker = todo_marker
|
|
|
|
prefix_len = #list_prefix
|
|
|
|
prefix_len = #todo_prefix
|
|
|
|
else
|
|
|
|
content = string.sub(line, prefix_len + 1) -- Content is everything after the matched prefix
|
|
|
|
-- No list marker, just the trimmed line content
|
|
|
|
else
|
|
|
|
content = vim.trim(line)
|
|
|
|
-- Not a TODO line, try matching a regular list prefix
|
|
|
|
prefix_len = #indent -- Length before the trimmed content
|
|
|
|
local list_prefix, list_indent, list_marker = string.match(line, list_prefix_pattern)
|
|
|
|
end
|
|
|
|
if list_prefix then
|
|
|
|
|
|
|
|
-- It's a regular list line
|
|
|
|
return {
|
|
|
|
is_todo = false
|
|
|
|
line = line,
|
|
|
|
indent = list_indent
|
|
|
|
indent = indent,
|
|
|
|
marker = list_marker
|
|
|
|
marker = marker_match, -- '-', '*', '+', or nil
|
|
|
|
prefix_len = #list_prefix
|
|
|
|
content = content,
|
|
|
|
content = string.sub(line, prefix_len + 1) -- Content is everything after the matched prefix
|
|
|
|
is_todo = is_todo,
|
|
|
|
else
|
|
|
|
prefix_len = prefix_len, -- Store the calculated prefix length
|
|
|
|
-- Not a list line either, treat as plain text
|
|
|
|
}
|
|
|
|
is_todo = false
|
|
|
|
|
|
|
|
indent = string.match(line, "^%s*") or ""
|
|
|
|
|
|
|
|
marker = nil
|
|
|
|
|
|
|
|
prefix_len = #indent
|
|
|
|
|
|
|
|
content = string.sub(line, prefix_len + 1) -- Use sub, trim might be too aggressive
|
|
|
|
|
|
|
|
-- content = vim.trim(line) -- Old way
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
|
|
|
line = line,
|
|
|
|
|
|
|
|
indent = indent,
|
|
|
|
|
|
|
|
marker = marker, -- '-', '*', '+', or nil
|
|
|
|
|
|
|
|
content = content,
|
|
|
|
|
|
|
|
is_todo = is_todo,
|
|
|
|
|
|
|
|
prefix_len = prefix_len, -- Store the calculated prefix length
|
|
|
|
|
|
|
|
}
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-- Helper: Gets details for the current line including cursor position
|
|
|
|
-- Helper: Gets details for the current line including cursor position
|
|
|
|
local function get_current_line_details_with_cursor()
|
|
|
|
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)
|
|
|
@ -68,7 +85,8 @@ local function add_todo_insert_mode()
|
|
|
|
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 "-" -- Default to '-' if no list marker found
|
|
|
|
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
|
|
|
|
-- Use the raw content calculated by the improved get_line_details
|
|
|
|
|
|
|
|
local text_content = details.content
|
|
|
|
|
|
|
|
|
|
|
|
local new_line = details.indent .. use_marker .. " [ ] " .. text_content
|
|
|
|
local new_line = details.indent .. use_marker .. " [ ] " .. text_content
|
|
|
|
-- Replace current line (lnum is 1-based, set_lines is 0-based)
|
|
|
|
-- Replace current line (lnum is 1-based, set_lines is 0-based)
|
|
|
@ -123,11 +141,13 @@ local function remove_todo_markup()
|
|
|
|
if not details or not details.is_todo then return end -- Only act on TODO lines
|
|
|
|
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] "
|
|
|
|
-- Construct the new line without the TODO markup "[ ] " or "[x] "
|
|
|
|
local new_line = details.indent .. details.marker .. " " .. details.content
|
|
|
|
-- Ensure marker exists, default to '-' if somehow nil (shouldn't happen if is_todo)
|
|
|
|
|
|
|
|
local use_marker = details.marker or "-"
|
|
|
|
|
|
|
|
local new_line = details.indent .. use_marker .. " " .. details.content
|
|
|
|
|
|
|
|
|
|
|
|
-- Calculate the position where the markup started
|
|
|
|
-- Calculate the position where the markup started
|
|
|
|
-- This is the length of the indent + marker + one space
|
|
|
|
-- This is the length of the indent + marker + one space
|
|
|
|
local markup_start_col = #(details.indent .. details.marker .. " ")
|
|
|
|
local markup_start_col = #(details.indent .. use_marker .. " ")
|
|
|
|
|
|
|
|
|
|
|
|
-- The markup itself is 4 characters: '[', ' ' or 'x', ']', ' '
|
|
|
|
-- The markup itself is 4 characters: '[', ' ' or 'x', ']', ' '
|
|
|
|
local markup_len = 4
|
|
|
|
local markup_len = 4
|
|
|
@ -162,7 +182,6 @@ local function setup_buffer_keymaps()
|
|
|
|
local function press_enter()
|
|
|
|
local function press_enter()
|
|
|
|
local details, lnum = get_current_line_details_with_cursor() -- Use basic helper is fine
|
|
|
|
local details, lnum = get_current_line_details_with_cursor() -- Use basic helper is fine
|
|
|
|
if not details then
|
|
|
|
if not details then
|
|
|
|
vim.notify("[Todoer Debug] press_enter: Failed to get details", vim.log.levels.WARN)
|
|
|
|
|
|
|
|
-- Getting details failed, do nothing and let Neovim handle Enter.
|
|
|
|
-- Getting details failed, do nothing and let Neovim handle Enter.
|
|
|
|
return
|
|
|
|
return
|
|
|
|
end
|
|
|
|
end
|
|
|
@ -171,16 +190,9 @@ local function setup_buffer_keymaps()
|
|
|
|
-- Use string.match with %S to check for any non-whitespace character
|
|
|
|
-- Use string.match with %S to check for any non-whitespace character
|
|
|
|
local has_content = details.content and string.match(details.content, "%S")
|
|
|
|
local has_content = details.content and string.match(details.content, "%S")
|
|
|
|
|
|
|
|
|
|
|
|
-- ===== DEBUGGING START =====
|
|
|
|
-- Debugging removed
|
|
|
|
vim.notify(string.format("[Todoer Debug] press_enter:\n is_todo: %s\n content: %s\n has_content (match %%S): %s",
|
|
|
|
|
|
|
|
tostring(details.is_todo),
|
|
|
|
|
|
|
|
vim.inspect(details.content), -- Use inspect to see quotes/details
|
|
|
|
|
|
|
|
tostring(has_content) -- Convert potential match result to string
|
|
|
|
|
|
|
|
), vim.log.levels.INFO)
|
|
|
|
|
|
|
|
-- ===== DEBUGGING END =====
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if details.is_todo and has_content then
|
|
|
|
if details.is_todo and has_content then
|
|
|
|
vim.notify("[Todoer Debug] press_enter: Entered BRANCH 1 (is_todo and has_content)", vim.log.levels.INFO)
|
|
|
|
|
|
|
|
-- Case 1: Non-empty TODO line
|
|
|
|
-- Case 1: Non-empty TODO line
|
|
|
|
-- Create a new TODO line below with same indent/marker
|
|
|
|
-- Create a new TODO line below with same indent/marker
|
|
|
|
local new_marker = details.marker or "-" -- Should always have marker if is_todo, but safety check
|
|
|
|
local new_marker = details.marker or "-" -- Should always have marker if is_todo, but safety check
|
|
|
@ -197,7 +209,6 @@ local function setup_buffer_keymaps()
|
|
|
|
-- No return value needed as it's not an expr mapping anymore.
|
|
|
|
-- No return value needed as it's not an expr mapping anymore.
|
|
|
|
|
|
|
|
|
|
|
|
elseif details.is_todo and not has_content then
|
|
|
|
elseif details.is_todo and not has_content then
|
|
|
|
vim.notify("[Todoer Debug] press_enter: Entered BRANCH 2 (is_todo and not has_content)", vim.log.levels.INFO)
|
|
|
|
|
|
|
|
-- Case 2: Empty TODO line (e.g., "- [ ]") - Implement Outdenting
|
|
|
|
-- Case 2: Empty TODO line (e.g., "- [ ]") - Implement Outdenting
|
|
|
|
local current_indent = details.indent
|
|
|
|
local current_indent = details.indent
|
|
|
|
local current_indent_len = #current_indent
|
|
|
|
local current_indent_len = #current_indent
|
|
|
@ -205,7 +216,6 @@ local function setup_buffer_keymaps()
|
|
|
|
local et = vim.bo.expandtab -- Get buffer's expandtab setting
|
|
|
|
local et = vim.bo.expandtab -- Get buffer's expandtab setting
|
|
|
|
|
|
|
|
|
|
|
|
if current_indent_len > 0 then
|
|
|
|
if current_indent_len > 0 then
|
|
|
|
vim.notify("[Todoer Debug] press_enter: Branch 2 - Outdenting", vim.log.levels.INFO)
|
|
|
|
|
|
|
|
-- Line is indented, calculate new indentation
|
|
|
|
-- Line is indented, calculate new indentation
|
|
|
|
local new_indent = ""
|
|
|
|
local new_indent = ""
|
|
|
|
if et then -- Using spaces
|
|
|
|
if et then -- Using spaces
|
|
|
@ -234,7 +244,6 @@ local function setup_buffer_keymaps()
|
|
|
|
-- Action handled, do not fall through.
|
|
|
|
-- Action handled, do not fall through.
|
|
|
|
|
|
|
|
|
|
|
|
else
|
|
|
|
else
|
|
|
|
vim.notify("[Todoer Debug] press_enter: Branch 2 - Terminating list", vim.log.levels.INFO)
|
|
|
|
|
|
|
|
-- Line is not indented, terminate the list
|
|
|
|
-- Line is not indented, terminate the list
|
|
|
|
-- Replace the line with an empty string
|
|
|
|
-- Replace the line with an empty string
|
|
|
|
vim.api.nvim_buf_set_lines(0, lnum - 1, lnum, false, { "" })
|
|
|
|
vim.api.nvim_buf_set_lines(0, lnum - 1, lnum, false, { "" })
|
|
|
@ -247,7 +256,6 @@ local function setup_buffer_keymaps()
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
else
|
|
|
|
else
|
|
|
|
vim.notify("[Todoer Debug] press_enter: Entered BRANCH 3 (Not a TODO or fallback)", vim.log.levels.INFO)
|
|
|
|
|
|
|
|
-- Case 3: Not a TODO line
|
|
|
|
-- Case 3: Not a TODO line
|
|
|
|
-- Do nothing. Neovim will automatically handle the Enter key press
|
|
|
|
-- Do nothing. Neovim will automatically handle the Enter key press
|
|
|
|
-- because this function didn't modify the buffer or explicitly handle it.
|
|
|
|
-- because this function didn't modify the buffer or explicitly handle it.
|
|
|
|