|  |  | @ -14,45 +14,62 @@ local function get_line_details(lnum) | 
			
		
	
		
		
			
				
					
					|  |  |  |     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 |  |  |  |     -- Try matching the TODO prefix first | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |     -- Content is everything after "[x] " or "[ ] " (including leading space) |  |  |  |     local todo_prefix, todo_indent, todo_marker, _ = string.match(line, todo_prefix_pattern) | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |     local todo_prefix = string.match(line, todo_pattern) -- The part like "- [ ] " |  |  |  | 
 | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |     content = string.match(line, todo_pattern .. "(.*)") or "" |  |  |  |     if todo_prefix then | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         -- It's a TODO line | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         is_todo = true | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         indent = todo_indent | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         marker = todo_marker | 
			
		
	
		
		
			
				
					
					|  |  |  |         prefix_len = #todo_prefix |  |  |  |         prefix_len = #todo_prefix | 
			
		
	
		
		
			
				
					
					|  |  |  |   elseif marker_match then |  |  |  |         content = string.sub(line, prefix_len + 1) -- Content is everything after the matched prefix | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |     -- Content is everything after "- " / "* " / "+ " |  |  |  |     else | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |     local list_prefix = prefix_match .. marker_match .. space_match -- The part like "- " |  |  |  |         -- Not a TODO line, try matching a regular list prefix | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |     content = string.match(line, marker_pattern .. "(.*)") or "" |  |  |  |         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 |  |  |  |             prefix_len = #list_prefix | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |             content = string.sub(line, prefix_len + 1) -- Content is everything after the matched prefix | 
			
		
	
		
		
			
				
					
					|  |  |  |         else |  |  |  |         else | 
			
		
	
		
		
			
				
					
					|  |  |  |     -- No list marker, just the trimmed line content |  |  |  |             -- Not a list line either, treat as plain text | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |     content = vim.trim(line) |  |  |  |             is_todo = false | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |     prefix_len = #indent -- Length before the trimmed content |  |  |  |             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 |  |  |  |     end | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     return { |  |  |  |     return { | 
			
		
	
		
		
			
				
					
					|  |  |  |         line = line, |  |  |  |         line = line, | 
			
		
	
		
		
			
				
					
					|  |  |  |         indent = indent, |  |  |  |         indent = indent, | 
			
		
	
		
		
			
				
					
					|  |  |  |     marker = marker_match, -- '-', '*', '+', or nil |  |  |  |         marker = marker, -- '-', '*', '+', or nil | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |         content = content, |  |  |  |         content = content, | 
			
		
	
		
		
			
				
					
					|  |  |  |         is_todo = is_todo, |  |  |  |         is_todo = is_todo, | 
			
		
	
		
		
			
				
					
					|  |  |  |         prefix_len = prefix_len, -- Store the calculated prefix length |  |  |  |         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. | 
			
		
	
	
		
		
			
				
					|  |  | 
 |