Browse Source

fix(defaults): Register standard commands and fix cursor updates

- Updated defaults.hpp to register all standard Emacs-like commands (next-line, previous-line, etc.) that were previously bound but not registered.
- Fixed 'insert-newline' and 'backward-delete-char' in defaults to use correct Lua property assignment for cursor position.
- Exposed 'set_cursor' explicitly in Lua API for compatibility.
- Added regression tests for default bindings (C-n, Return).
Bernardo Magri 1 month ago
parent
commit
3a92acee04
3 changed files with 117 additions and 31 deletions
  1. 75 30
      src/defaults.hpp
  2. 1 0
      src/lua_api.cpp
  3. 41 1
      tests/test_defaults_loading.cpp

+ 75 - 30
src/defaults.hpp

@@ -7,11 +7,11 @@ constexpr const char* LUA_DEFAULTS = R"(
 -- Lumacs Embedded Defaults
 -- =========================================================
 -- This runs BEFORE the user's init.lua.
+-- It establishes the core editing environment.
 
 -- 1. Sane Editor Config
 editor.config:set("show_line_numbers", true)
 editor.config:set("tab_width", 4)
-editor.config:set("debug_lua", false)
 
 -- 2. Mode Infrastructure
 local major_modes = {}
@@ -59,6 +59,9 @@ function activate_major_mode(mode_name)
         mode.highlight()
     end
     
+    -- Register mode-specific keybindings
+    -- Note: This is a simple implementation that binds globally for now.
+    -- A proper implementation would use a keymap stack.
     for key, func in pairs(mode.keybindings) do
         editor:bind_key(key, func)
     end
@@ -86,23 +89,44 @@ function auto_activate_major_mode()
             end
         end
     end
+    activate_major_mode("fundamental-mode")
 end
 
--- 3. Basic Editing Commands
+-- 3. Basic Editing Commands & Registration
+
+-- Navigation
+editor:register_command("next-line", "Move cursor down", function() editor:move_down() end, true)
+editor:register_command("previous-line", "Move cursor up", function() editor:move_up() end, true)
+editor:register_command("forward-char", "Move cursor right", function() editor:move_right() end, true)
+editor:register_command("backward-char", "Move cursor left", function() editor:move_left() end, true)
+editor:register_command("forward-word", "Move forward one word", function() editor:move_forward_word() end, true)
+editor:register_command("backward-word", "Move backward one word", function() editor:move_backward_word() end, true)
+editor:register_command("move-beginning-of-line", "Go to beginning of line", function() editor:move_to_line_start() end, true)
+editor:register_command("move-end-of-line", "Go to end of line", function() editor:move_to_line_end() end, true)
+editor:register_command("beginning-of-buffer", "Go to beginning of buffer", function() editor:goto_beginning() end, true)
+editor:register_command("end-of-buffer", "Go to end of buffer", function() editor:goto_end() end, true)
+editor:register_command("scroll-up-command", "Page down", function() editor:page_down() end, true)
+editor:register_command("scroll-down-command", "Page up", function() editor:page_up() end, true)
+
+-- Insertion / Deletion
 function lumacs_insert_newline()
     local cursor = editor.cursor
     editor.buffer:insert_newline(cursor)
-    editor:set_cursor(lumacs.Position(cursor.line + 1, 0))
+    -- Move to start of next line
+    editor.cursor = lumacs.Position(cursor.line + 1, 0)
 end
 editor:register_command("insert-newline", "Insert newline", lumacs_insert_newline, true)
 
 function lumacs_backward_delete_char()
     local cursor = editor.cursor
     if cursor.line == 0 and cursor.column == 0 then return end
+    
     editor.buffer:erase_char(cursor)
-    -- Cursor adjustment is handled by buffer logic usually, but let's be safe
+    
+    -- Calculate new cursor position
     local new_pos = cursor
-    if new_pos.column > 0 then new_pos = lumacs.Position(new_pos.line, new_pos.column - 1)
+    if new_pos.column > 0 then 
+        new_pos = lumacs.Position(new_pos.line, new_pos.column - 1)
     elseif new_pos.line > 0 then
         local prev_line_len = #editor.buffer:line(new_pos.line - 1)
         new_pos = lumacs.Position(new_pos.line - 1, prev_line_len)
@@ -125,16 +149,41 @@ function self_insert_command(args)
 end
 editor:register_command("self-insert-command", "Insert char", self_insert_command, true)
 
--- 4. Keybindings
-editor:bind_key("Return", lumacs_insert_newline)
-editor:bind_key("Backspace", lumacs_backward_delete_char)
-editor:bind_key("Delete", lumacs_delete_char)
+-- Clipboard / Kill Ring
+editor:register_command("kill-line", "Kill rest of line", function() editor:kill_line() end, true)
+editor:register_command("kill-region", "Kill selected region", function() editor:kill_region() end, true)
+editor:register_command("copy-region-as-kill", "Copy region", function() editor:copy_region_as_kill() end, true)
+editor:register_command("yank", "Paste", function() editor:yank() end, true)
+editor:register_command("yank-pop", "Cycle paste", function() editor:yank_pop() end, true)
+
+-- Undo/Redo
+editor:register_command("undo", "Undo", function() editor:undo() end, true)
+editor:register_command("redo", "Redo", function() editor:redo() end, true)
+
+-- File/Buffer
+editor:register_command("save-buffer", "Save current buffer", function() 
+    if editor.buffer:save() then editor:message("Saved") else editor:message("Failed to save") end 
+end, true)
+editor:register_command("find-file", "Open file", function() editor:find_file_mode() end, true, "f")
+editor:register_command("switch-buffer", "Switch buffer", function() editor:buffer_switch_mode() end, true, "b")
+editor:register_command("kill-buffer", "Kill buffer", function() editor:kill_buffer_mode() end, true, "b")
+editor:register_command("save-buffers-kill-terminal", "Quit", function() editor:quit() end, true)
+editor:register_command("execute-extended-command", "M-x", function() editor:command_mode() end, true)
+
+-- Macros
+editor:register_command("start-kbd-macro", "Start recording macro", function() editor:start_kbd_macro() end, true)
+editor:register_command("end-kbd-macro-or-call", "End recording or call macro", function() editor:end_kbd_macro_or_call() end, true)
+
+-- 4. Default Keybindings
+editor:bind_key("Return", "insert-newline")
+editor:bind_key("Backspace", "backward-delete-char")
+editor:bind_key("Delete", "delete-char")
 
 -- Arrow Keys
-editor:bind_key("ArrowUp", function() editor:move_up() end)
-editor:bind_key("ArrowDown", function() editor:move_down() end)
-editor:bind_key("ArrowLeft", function() editor:move_left() end)
-editor:bind_key("ArrowRight", function() editor:move_right() end)
+editor:bind_key("ArrowUp", "previous-line")
+editor:bind_key("ArrowDown", "next-line")
+editor:bind_key("ArrowLeft", "backward-char")
+editor:bind_key("ArrowRight", "forward-char")
 
 -- Emacs Navigation
 editor:bind_key("C-f", "forward-char")
@@ -156,13 +205,19 @@ editor:bind_key("C-x C-s", "save-buffer")
 editor:bind_key("C-x C-f", "find-file")
 editor:bind_key("C-x b", "switch-buffer")
 editor:bind_key("C-x k", "kill-buffer")
-editor:bind_key("C-x 0", "delete-window")
-editor:bind_key("C-x 1", "delete-other-windows")
-editor:bind_key("C-x 2", "split-window-below")
-editor:bind_key("C-x 3", "split-window-right")
-editor:bind_key("C-x o", "other-window")
+editor:bind_key("C-x 0", "delete-window") -- Need to implement
+editor:bind_key("C-x 1", "delete-other-windows") -- Need to implement
+editor:bind_key("C-x 2", "split-window-below") -- Need to implement
+editor:bind_key("C-x 3", "split-window-right") -- Need to implement
+editor:bind_key("C-x o", "other-window") -- Need to implement
 editor:bind_key("M-x", "execute-extended-command")
 
+-- Windows Implementation
+editor:register_command("delete-window", "Close window", function() editor:close_window() end, true)
+editor:register_command("split-window-below", "Split horizontal", function() editor:split_horizontally() end, true)
+editor:register_command("split-window-right", "Split vertical", function() editor:split_vertically() end, true)
+editor:register_command("other-window", "Next window", function() editor:next_window() end, true)
+
 -- Undo/Redo
 editor:bind_key("C-/", "undo")
 editor:bind_key("C-_", "undo")
@@ -180,18 +235,8 @@ editor:bind_key("C-k", "kill-line")
 editor:bind_key("F3", "start-kbd-macro")
 editor:bind_key("F4", "end-kbd-macro-or-call")
 
--- Register Basic Commands (Wrappers for C++ commands)
-editor:register_command("save-buffer", "Save current buffer", function() if editor.buffer:save() then editor:message("Saved") else editor:message("Failed to save") end end, true, "b")
-editor:register_command("find-file", "Open file", function() editor:find_file_mode() end, true, "f")
-editor:register_command("switch-buffer", "Switch buffer", function() editor:buffer_switch_mode() end, true, "b")
-editor:register_command("kill-buffer", "Kill buffer", function() editor:kill_buffer_mode() end, true, "b")
-editor:register_command("start-kbd-macro", "Start recording macro", function() editor:start_kbd_macro() end, true)
-editor:register_command("end-kbd-macro-or-call", "End recording or call macro", function() editor:end_kbd_macro_or_call() end, true)
-editor:register_command("execute-extended-command", "M-x", function() editor:command_mode() end, true)
-editor:register_command("save-buffers-kill-terminal", "Quit", function() editor:quit() end, true)
-
--- Auto-activate mode for initial buffer
+-- Auto-activate mode
 auto_activate_major_mode()
 )";
 
-} // namespace lumacs
+} // namespace lumacs

+ 1 - 0
src/lua_api.cpp

@@ -660,6 +660,7 @@ void LuaApi::register_types() {
         sol::no_constructor,
 
         "cursor", sol::property(&EditorCore::cursor, &EditorCore::set_cursor), // Added cursor property
+        "set_cursor", &EditorCore::set_cursor, // Explicit setter method
         
         // Movement
         "move_up", &EditorCore::move_up,

+ 41 - 1
tests/test_defaults_loading.cpp

@@ -3,6 +3,7 @@
 #include "lumacs/lua_api.hpp"
 #include "lumacs/keybinding.hpp"
 #include "lumacs/command_system.hpp"
+#include "lumacs/window.hpp"
 #include <memory>
 
 using namespace lumacs;
@@ -15,8 +16,14 @@ protected:
         // Load the REAL defaults (this is what main.cpp does)
         core->lua_api()->load_init_file();
         
-        // Ensure buffer is ready
+        // Ensure buffer is ready and attached to window
         core->new_buffer("*scratch*");
+        
+        // Ensure we have an active window (Core constructor makes one, new_buffer assigns buffer to it)
+        if (!core->active_window()) {
+             // This shouldn't happen given EditorCore ctor logic, but safe check
+        }
+        
         core->set_cursor({0,0});
     }
 
@@ -31,6 +38,8 @@ TEST_F(DefaultsLoadingTest, SelfInsertFromDefaults) {
     std::string input_key = "h";
     
     // 1. Process key (should be unbound in defaults)
+    // Wait, 'h' is unbound. fallback triggers self-insert-command.
+    // KeyBindingManager returns Unbound.
     auto result = core->keybinding_manager().process_key(input_key);
     EXPECT_EQ(result.type, KeyResult::Unbound);
     
@@ -47,3 +56,34 @@ TEST_F(DefaultsLoadingTest, SelfInsertFromDefaults) {
     // 3. Verify state
     EXPECT_EQ(core->buffer().content(), "h");
 }
+
+TEST_F(DefaultsLoadingTest, NextLineCommand) {
+    // Setup: 2 lines
+    core->buffer().insert({0,0}, "Line 1\nLine 2");
+    core->set_cursor({0,0});
+    
+    // Press C-n
+    // "C-n" -> "next-line" -> editor:move_down()
+    auto result = core->keybinding_manager().process_key("C-n");
+    
+    EXPECT_EQ(result.type, KeyResult::Executed);
+    
+    // Check cursor position. Should be line 1.
+    EXPECT_EQ(core->cursor().line, 1);
+}
+
+TEST_F(DefaultsLoadingTest, ReturnKeyNewline) {
+    core->buffer().insert({0,0}, "Line1");
+    core->set_cursor({0,5}); // End of line
+    
+    // Press Return
+    // "Return" -> "insert-newline"
+    auto result = core->keybinding_manager().process_key("Return");
+    EXPECT_EQ(result.type, KeyResult::Executed);
+    
+    // Check content
+    EXPECT_EQ(core->buffer().content(), "Line1\n");
+    // Check cursor (should be line 1, col 0)
+    EXPECT_EQ(core->cursor().line, 1);
+    EXPECT_EQ(core->cursor().column, 0);
+}