Prechádzať zdrojové kódy

fix(editing): Correct backward-delete-char logic and cursor movement

Bernardo Magri 1 mesiac pred
rodič
commit
3c99e107bf
1 zmenil súbory, kde vykonal 103 pridanie a 83 odobranie
  1. 103 83
      init.lua

+ 103 - 83
init.lua

@@ -2,7 +2,11 @@
 -- This file is executed on startup and allows you to customize keybindings,
 -- create commands, and extend the editor with Lua.
 
-editor:message("Loading init.lua...")
+-- Lumacs Configuration File
+-- This file is executed on startup and allows you to customize keybindings,
+-- create commands, and extend the editor with Lua.
+
+-- editor:message("Loading init.lua...")
 
 -- ============================================================================
 -- MODE SYSTEM (Emacs-style Major and Minor Modes)
@@ -314,18 +318,35 @@ editor:bind_key("Return", lumacs_insert_newline)
 editor:register_command("insert-newline", "Insert a new line at cursor position.", lumacs_insert_newline, true)
 
 function lumacs_backward_delete_char()
-    local cursor = editor.cursor
-    editor.buffer:erase_char(cursor)
-    if cursor.column > 0 then
-        editor:set_cursor(lumacs.Position(cursor.line, cursor.column - 1))
-    elseif cursor.line > 0 then
-        local prev_line_len = #editor.buffer:line(cursor.line - 1)
-        editor:set_cursor(lumacs.Position(cursor.line - 1, prev_line_len))
+    local cursor_original = editor.cursor
+
+    -- If at (0,0), nothing to delete before it.
+    if cursor_original.column == 0 and cursor_original.line == 0 then
+        return
     end
+
+    -- Call erase_char with the current cursor position.
+    -- erase_char will internally delete the char at (pos.column - 1).
+    editor.buffer:erase_char(cursor_original) 
+    
+    -- The cursor position should be adjusted *after* the erase.
+    -- This is the same logic as before, calculating the new cursor position.
+    local new_cursor_pos = cursor_original
+    if new_cursor_pos.column > 0 then
+        new_cursor_pos = lumacs.Position(new_cursor_pos.line, new_cursor_pos.column - 1)
+    elseif new_cursor_pos.line > 0 then
+        -- If line was joined, new cursor is at end of previous line.
+        local prev_line_len = #editor.buffer:line(new_cursor_pos.line - 1)
+        new_cursor_pos = lumacs.Position(new_cursor_pos.line - 1, prev_line_len)
+    end
+    editor.cursor = new_cursor_pos
 end
 editor:bind_key("Backspace", lumacs_backward_delete_char)
 editor:register_command("backward-delete-char", "Delete the character before cursor.", lumacs_backward_delete_char, true)
 
+editor:bind_key("C-m", lumacs_backward_delete_char)
+editor:register_command("backward-delete-char", "Delete the character before cursor.", lumacs_backward_delete_char, true)
+
 function lumacs_delete_char()
     local cursor = editor.cursor
     editor.buffer:erase_char(lumacs.Position(cursor.line, cursor.column + 1))
@@ -344,14 +365,13 @@ editor:bind_key("End", function() editor:move_to_line_end() end)
 
 -- Generic self-insert command for printable characters
 -- This command is special; it's called by the C++ core if no other binding matches a printable char.
-function self_insert_command(char_to_insert)
+function self_insert_command(args)
+    local char_to_insert = args[1]
+    if not char_to_insert then return end
+
     local cursor = editor.cursor
     editor.buffer:insert(cursor, char_to_insert)
-    editor:set_cursor(lumacs.Position(cursor.line, cursor.column + 1))
-    -- Optionally, log for debugging
-    -- local new_cursor = editor.cursor
-    -- editor:message(string.format("[DEBUG Lua] Inserted '%s' at (%d,%d) -> cursor now at (%d,%d)", 
-    --                    char_to_insert, cursor.line, cursor.column, new_cursor.line, new_cursor.column))
+    editor:move_right()
 end
 editor:register_command("self-insert-command", "Insert the character pressed.", self_insert_command, true)
 
@@ -466,11 +486,11 @@ function find_next(query)
     -- Otherwise we might find the same one if we are sitting on it.
     -- A simple way is to advance column by 1 for the search start.
     local search_start = lumacs.Position(cursor.line, cursor.column + 1)
-    
+
     -- If at end of line, search from start of next line is handled by find() implementation?
     -- Buffer::find currently implements simple linear search from a position.
     -- If column is beyond end, it should handle it. Let's trust the C++ impl or adjust.
-    
+
     local res = buf:find(query, search_start)
     if res then
         editor.cursor = res.start
@@ -818,7 +838,7 @@ editor:bind_key("C-x C-b", function()
 
     local list_text = table.concat(lines, "\n")
     local list_buf_name = "*Buffer List*"
-    
+
     -- Switch to or create buffer
     local list_buf = editor:get_buffer_by_name(list_buf_name)
     if list_buf then
@@ -826,12 +846,12 @@ editor:bind_key("C-x C-b", function()
     else
         editor:new_buffer(list_buf_name)
     end
-    
+
     local buf = editor.buffer
     buf:clear()
     buf:insert(lumacs.Position(0,0), list_text)
     editor:goto_beginning()
-    
+
     editor:message(string.format("Buffer list (%d buffers)", #buffer_info))
 end)
 
@@ -844,10 +864,10 @@ editor:bind_key("M-u", function()
     local start_pos = editor.cursor
     editor:move_forward_word()
     local end_pos = editor.cursor
-    
+
     local range = lumacs.Range(start_pos, end_pos)
     local text = editor.buffer:get_text_in_range(range)
-    
+
     if #text > 0 then
         editor.buffer:replace(range, string.upper(text))
     end
@@ -858,10 +878,10 @@ editor:bind_key("M-l", function()
     local start_pos = editor.cursor
     editor:move_forward_word()
     local end_pos = editor.cursor
-    
+
     local range = lumacs.Range(start_pos, end_pos)
     local text = editor.buffer:get_text_in_range(range)
-    
+
     if #text > 0 then
         editor.buffer:replace(range, string.lower(text))
     end
@@ -872,10 +892,10 @@ editor:bind_key("M-c", function()
     local start_pos = editor.cursor
     editor:move_forward_word()
     local end_pos = editor.cursor
-    
+
     local range = lumacs.Range(start_pos, end_pos)
     local text = editor.buffer:get_text_in_range(range)
-    
+
     if #text > 0 then
         local cap_text = text:sub(1,1):upper() .. text:sub(2):lower()
         editor.buffer:replace(range, cap_text)
@@ -889,7 +909,7 @@ editor:bind_key("C-x C-u", function()
         editor:message("No active region")
         return
     end
-    
+
     local text = editor.buffer:get_text_in_range(range)
     if #text > 0 then
         editor.buffer:replace(range, string.upper(text))
@@ -904,7 +924,7 @@ editor:bind_key("C-x C-l", function()
         editor:message("No active region")
         return
     end
-    
+
     local text = editor.buffer:get_text_in_range(range)
     if #text > 0 then
         editor.buffer:replace(range, string.lower(text))
@@ -925,14 +945,14 @@ function comment_dwim()
     local mode = major_modes[mode_name]
     local prefix = mode and mode.comment_syntax or "--"
     local escaped_prefix = escape_pattern(prefix)
-    
+
     local range = editor.buffer:get_region(editor.cursor)
-    
+
     if range then
         -- Region handling
         local start_line = range.start.line
         local end_line = range["end"].line
-        
+
         -- Check if all lines are commented
         local all_commented = true
         for i = start_line, end_line do
@@ -943,12 +963,12 @@ function comment_dwim()
                 break
             end
         end
-        
+
         -- Apply change
         for i = start_line, end_line do
             local line = editor.buffer:line(i)
             local line_range = lumacs.Range(lumacs.Position(i, 0), lumacs.Position(i, #line))
-            
+
             if #line > 0 then
                 if all_commented then
                     -- Uncomment
@@ -970,13 +990,13 @@ function comment_dwim()
                 end
             end
         end
-        
+
     else
         -- Single line
         local line_num = editor.cursor.line
         local line = editor.buffer:line(line_num)
         local line_range = lumacs.Range(lumacs.Position(line_num, 0), lumacs.Position(line_num, #line))
-        
+
         if string.match(line, "^%s*" .. escaped_prefix) then
             -- Uncomment
             local s, e = string.find(line, escaped_prefix)
@@ -996,7 +1016,7 @@ function comment_dwim()
     end
 end
 
-editor:bind_key("M-;") comment_dwim)
+editor:bind_key("M-;", comment_dwim)
 
 -- ============================================================================
 -- REGISTERS (C-x r s, C-x r i)
@@ -1017,14 +1037,14 @@ editor:bind_key("C-x r s", function()
     editor:copy_region_to_register(register_char)
 end)
 
--- C-x r i (insert-register) - Insert from register  
+-- C-x r i (insert-register) - Insert from register
 editor:bind_key("C-x r i", function()
     local register_char = get_register_char("Insert register:")
     editor:yank_from_register(register_char)
 end)
 
 -- ============================================================================
--- RECTANGLES (C-x r k/y/t) 
+-- RECTANGLES (C-x r k/y/t)
 -- ============================================================================
 
 -- C-x r k (kill-rectangle) - Cut rectangular region
@@ -1032,7 +1052,7 @@ editor:bind_key("C-x r k", function()
     editor:kill_rectangle()
 end)
 
--- C-x r y (yank-rectangle) - Paste rectangular region  
+-- C-x r y (yank-rectangle) - Paste rectangular region
 editor:bind_key("C-x r y", function()
     editor:yank_rectangle()
 end)
@@ -1089,7 +1109,7 @@ function toggle_line_numbers()
     local config = editor.config
     local current = config:get_bool("show_line_numbers", true)
     config:set("show_line_numbers", not current)
-    
+
     if not current then
         editor:message("Line numbers enabled")
     else
@@ -1140,32 +1160,32 @@ auto_activate_major_mode()
 
 
 -- File and Buffer Commands
-editor:register_command("save-buffer", "Save current buffer", function(args) 
-    if editor.buffer:save() then 
+editor:register_command("save-buffer", "Save current buffer", function(args)
+    if editor.buffer:save() then
         editor:message("Saved " .. editor.buffer:name())
-    else 
-        editor:message("Save failed") 
-    end 
+    else
+        editor:message("Save failed")
+    end
 end, true, "b") -- "b" means prompt for existing buffer name
 
-editor:register_command("find-file", "Find file", function(args) 
-    editor:find_file_mode() 
+editor:register_command("find-file", "Find file", function(args)
+    editor:find_file_mode()
 end, true, "f") -- "f" means prompt for file name
 
-editor:register_command("kill-buffer", "Kill buffer", function(args) 
-    editor:kill_buffer_mode() 
+editor:register_command("kill-buffer", "Kill buffer", function(args)
+    editor:kill_buffer_mode()
 end, true, "b")
 
-editor:register_command("switch-buffer", "Switch buffer", function(args) 
-    editor:buffer_switch_mode() 
+editor:register_command("switch-buffer", "Switch buffer", function(args)
+    editor:buffer_switch_mode()
 end, true, "b")
 
 -- Alias for switch-buffer to match Emacs expectations
-editor:register_command("switch-to-buffer", "Switch buffer (Alias)", function(args) 
-    editor:buffer_switch_mode() 
+editor:register_command("switch-to-buffer", "Switch buffer (Alias)", function(args)
+    editor:buffer_switch_mode()
 end, true, "b")
 
-editor:register_command("list-buffers", "List all buffers", function(args) 
+editor:register_command("list-buffers", "List all buffers", function(args)
     local buffer_info = editor:get_all_buffer_info()
     if #buffer_info == 0 then
         editor:message("No buffers open")
@@ -1197,7 +1217,7 @@ editor:register_command("list-buffers", "List all buffers", function(args)
     editor.buffer:clear()
     editor.buffer:insert(lumacs.Position(0,0), list_text)
     editor:goto_beginning()
-    
+
     return {success = true, message = string.format("Buffer list (%d buffers)", #buffer_info)}
 end, false) -- Not typically interactive through prompt
 
@@ -1219,7 +1239,7 @@ editor:register_command("split-window-below", "Split window horizontally", funct
 editor:register_command("split-window-right", "Split window vertically", function() editor:split_vertically() end, true)
 editor:register_command("delete-window", "Close current window", function() editor:close_window() end, true)
 editor:register_command("other-window", "Select other window", function() editor:next_window() end, true)
-editor:register_command("delete-other-windows", "Delete all other windows", function() 
+editor:register_command("delete-other-windows", "Delete all other windows", function()
     -- Simplified: keep closing others until only 1?
     -- Or just implement properly in C++ later.
     -- For now, assume users use C-x 1 if implemented?
@@ -1252,14 +1272,14 @@ editor:register_command("describe-mode", "Show current major and minor modes", f
     local mode_name = current_major_mode()
     local buf = editor.buffer
     local buf_name = buf:name()
-    
+
     local minor_list = {}
     if buffer_minor_modes[buf_name] then
         for mode, _ in pairs(buffer_minor_modes[buf_name]) do
             table.insert(minor_list, mode)
         end
     end
-    
+
     local minor_str = #minor_list > 0 and table.concat(minor_list, ", ") or "none"
     return {success = true, message = string.format("Major: %s | Minor: %s", mode_name, minor_str)}
 end, false)
@@ -1267,7 +1287,7 @@ end, false)
 editor:register_command("count-lines", "Count lines in buffer or region", function(args)
     local buf = editor.buffer
     local region = buf:get_region(editor.cursor)
-    
+
     if region then
         local lines = region["end"].line - region.start.line + 1
         return {success = true, message = string.format("Region has %d lines", lines)}
@@ -1281,18 +1301,18 @@ editor:register_command("word-count", "Count words in buffer or region", functio
     local buf = editor.buffer
     local region = buf:get_region(editor.cursor)
     local text
-    
+
     if region then
         text = buf:get_text_in_range(region)
     else
         text = buf:get_all_text()
     end
-    
+
     local words = 0
     for word in text:gmatch("%S+") do
         words = words + 1
     end
-    
+
     local target = region and "region" or "buffer"
     return {success = true, message = string.format("%s has %d words", target, words)}
 end, false)
@@ -1301,19 +1321,19 @@ editor:register_command("goto-char", "Go to character position", function(args)
     if #args == 0 then
         return {success = false, message = "Character position required"}
     end
-    
+
     local pos = tonumber(args[1])
     if not pos then
         return {success = false, message = "Invalid character position: " .. args[1]}
     end
-    
+
     local buf = editor.buffer
     local text = buf:get_all_text()
-    
+
     if pos < 1 or pos > #text then
         return {success = false, message = "Position out of range"}
     end
-    
+
     -- Convert character position to line/column
     local line = 0
     local col = 0
@@ -1325,7 +1345,7 @@ editor:register_command("goto-char", "Go to character position", function(args)
             col = col + 1
         end
     end
-    
+
     editor.cursor = lumacs.Position(line, col)
     return {success = true, message = string.format("Moved to character %d (line %d, column %d)", pos, line + 1, col + 1)}
 end, true, "n")
@@ -1347,15 +1367,15 @@ end, false)
 editor:register_command("revert-buffer", "Reload buffer from file", function(args)
     local buf = editor.buffer
     local filepath = buf:filepath()
-    
+
     if not filepath then
         return {success = false, message = "Buffer is not visiting a file"}
     end
-    
+
     if buf:is_modified() then
         return {success = false, message = "Buffer has unsaved changes"}
     end
-    
+
     if editor:load_file(filepath) then
         return {success = true, message = "Reverted " .. buf:name()}
     else
@@ -1367,16 +1387,16 @@ editor:register_command("rename-buffer", "Rename current buffer", function(args)
     if #args == 0 then
         return {success = false, message = "New buffer name required"}
     end
-    
+
     local new_name = args[1]
     local buf = editor.buffer
     local old_name = buf:name()
-    
+
     -- Check if name is already taken
     if editor:get_buffer_by_name(new_name) then
         return {success = false, message = "Buffer name already exists: " .. new_name}
     end
-    
+
     buf:set_name(new_name)
     return {success = true, message = string.format("Renamed buffer '%s' to '%s'", old_name, new_name)}
 end, true, "s") -- "s" means prompt for string
@@ -1386,14 +1406,14 @@ editor:register_command("eval-expression", "Evaluate Lua expression", function(a
     if #args == 0 then
         return {success = false, message = "Lua expression required"}
     end
-    
+
     local expr = table.concat(args, " ")
     local func, err = load("return " .. expr)
-    
+
     if not func then
         return {success = false, message = "Parse error: " .. err}
     end
-    
+
     local success, result = pcall(func)
     if success then
         return {success = true, message = tostring(result)}
@@ -1406,7 +1426,7 @@ end, true, "s")
 editor:register_command("auto-theme", "Automatically set theme based on time of day", function(args)
     local hour = tonumber(os.date("%H"))
     local theme_name
-    
+
     if hour >= 6 and hour < 18 then
         -- Daytime: use light theme
         theme_name = "gruvbox-light"
@@ -1417,7 +1437,7 @@ editor:register_command("auto-theme", "Automatically set theme based on time of
         -- Night: use dark theme
         theme_name = "nord"
     end
-    
+
     local success, message = editor:execute_command("set-theme", {theme_name})
     if success then
         return {success = true, message = string.format("Auto-selected %s theme for %d:00", theme_name, hour)}
@@ -1429,7 +1449,7 @@ end, false)
 editor:register_command("theme-demo", "Demonstrate theme switching", function(args)
     local themes = {"solarized-dark", "nord", "gruvbox-light", "dracula"}
     local demo_msg = "Theme demo - switching through: " .. table.concat(themes, ", ")
-    
+
     -- This would ideally be enhanced with a timer to show themes in sequence
     -- For now, just switch to a demo theme
     local success, message = editor:execute_command("set-theme", {themes[1]})
@@ -1447,7 +1467,7 @@ end, false)
 -- Returns a list of completion candidates based on the current mode and input
 function get_completion_candidates(mode_name, input)
     local candidates = {}
-    
+
     if mode_name == "Command" then
         -- Command completion (M-x)
         for name, _ in pairs(lumacs.command_registry) do
@@ -1456,7 +1476,7 @@ function get_completion_candidates(mode_name, input)
             end
         end
         table.sort(candidates)
-    
+
     elseif mode_name == "BufferSwitch" or mode_name == "KillBuffer" then
         -- Buffer name completion
         local buffers = editor:get_buffer_names()
@@ -1466,7 +1486,7 @@ function get_completion_candidates(mode_name, input)
             end
         end
         table.sort(candidates)
-        
+
     elseif mode_name == "FindFile" then
         -- File path completion (simple version)
         -- Note: Full file system completion is complex to do in pure Lua without bindings
@@ -1474,6 +1494,6 @@ function get_completion_candidates(mode_name, input)
         -- For now, we'll return empty list or maybe just current directory files if exposed.
         -- Since we don't have 'ls' exposed, we can't do much here yet without C++ help.
     end
-    
+
     return candidates
-end
+end