diff --git a/lua/todoer/init.lua b/lua/todoer/init.lua index 13f5695..34b8632 100644 --- a/lua/todoer/init.lua +++ b/lua/todoer/init.lua @@ -9,50 +9,67 @@ local defaults = { -- 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 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 - -- Matches lines like: "- [ ] task", "* [x] task", "+ [ ] task" - local todo_pattern = "^(%s*[%-%*%+]%s+)%[([ x])%]%s*" -- Capture prefix before checkbox - - local prefix_match, marker_match, space_match = string.match(line, marker_pattern) - local is_todo = (string.match(line, todo_pattern) ~= nil) - local content = "" - local prefix_len = 0 -- Length of the string before the content starts - - if is_todo then - -- 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 "" - prefix_len = #todo_prefix - elseif marker_match then - -- Content is everything after "- " / "* " / "+ " - local list_prefix = prefix_match .. marker_match .. space_match -- The part like "- " - content = string.match(line, marker_pattern .. "(.*)") or "" - prefix_len = #list_prefix - else - -- No list marker, just the trimmed line content - content = vim.trim(line) - prefix_len = #indent -- Length before the trimmed content - end - - return { - line = line, - indent = indent, - marker = marker_match, -- '-', '*', '+', or nil - content = content, - is_todo = is_todo, - prefix_len = prefix_len, -- Store the calculated prefix length - } + -- 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] + + -- Define patterns + -- Pattern to capture the full TODO prefix, including trailing space(s) + -- Captures: 1: Full prefix, 2: Indent, 3: Marker, 4: Checkbox state + local todo_prefix_pattern = "^(%s*([%-%*%+])%s+%[([ x])%]%s*)" + -- Pattern to capture a regular list prefix + -- Captures: 1: Full prefix, 2: Indent, 3: Marker + local list_prefix_pattern = "^(%s*([%-%*%+])%s+)" + + local indent = "" + local marker = nil + local content = "" + local is_todo = false + local prefix_len = 0 + + -- Try matching the TODO prefix first + local todo_prefix, todo_indent, todo_marker, _ = string.match(line, todo_prefix_pattern) + + if todo_prefix then + -- It's a TODO line + is_todo = true + indent = todo_indent + marker = todo_marker + prefix_len = #todo_prefix + content = string.sub(line, prefix_len + 1) -- Content is everything after the matched prefix + else + -- Not a TODO line, try matching a regular list prefix + local list_prefix, list_indent, list_marker = string.match(line, list_prefix_pattern) + if list_prefix then + -- It's a regular list line + is_todo = false + indent = list_indent + marker = list_marker + prefix_len = #list_prefix + content = string.sub(line, prefix_len + 1) -- Content is everything after the matched prefix + else + -- 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 + -- Helper: Gets details for the current line including cursor position local function get_current_line_details_with_cursor() 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 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 -- 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 -- 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 -- 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', ']', ' ' local markup_len = 4 @@ -162,7 +182,6 @@ local function setup_buffer_keymaps() local function press_enter() local details, lnum = get_current_line_details_with_cursor() -- Use basic helper is fine 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. return end @@ -171,16 +190,9 @@ local function setup_buffer_keymaps() -- Use string.match with %S to check for any non-whitespace character local has_content = details.content and string.match(details.content, "%S") - -- ===== DEBUGGING START ===== - 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 ===== + -- Debugging removed 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 -- 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 @@ -197,7 +209,6 @@ local function setup_buffer_keymaps() -- No return value needed as it's not an expr mapping anymore. 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 local current_indent = details.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 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 local new_indent = "" if et then -- Using spaces @@ -234,7 +244,6 @@ local function setup_buffer_keymaps() -- Action handled, do not fall through. else - vim.notify("[Todoer Debug] press_enter: Branch 2 - Terminating list", vim.log.levels.INFO) -- Line is not indented, terminate the list -- Replace the line with an empty string vim.api.nvim_buf_set_lines(0, lnum - 1, lnum, false, { "" }) @@ -247,7 +256,6 @@ local function setup_buffer_keymaps() end 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 -- Do nothing. Neovim will automatically handle the Enter key press -- because this function didn't modify the buffer or explicitly handle it.