Browse Source

fix(tui): enable space key input and fix C++ syntax highlighting

This commit fixes two critical TUI issues that prevented proper text
editing and syntax highlighting functionality.

Changes to TUI input handling (src/tui_editor.cpp):
- Add special case handling for "Space" key in self-insert fallback
- Previously only single-character keys (length == 1) were handled
- Now explicitly checks for "Space" and converts to " " for insertion
- This enables typing spaces in all major modes without explicit bindings

Changes to C++ mode highlighting (lua/major_modes/c_cpp_mode.lua):
- Fix escape_pattern function (removed corrupted character)
- Replace regex-style string patterns with Lua pattern syntax
  - '"[^"\\]*(?:\\.[^"\\]*)*"' → '"[^"]*"' (simplified but functional)
  - Similar fix for single-quoted strings
- Simplify number matching pattern
- Replace combined keyword/type pattern matching with individual iteration
  - This fixes the non-working gmatch on alternation patterns
  - Now properly highlights keywords: if, for, while, class, etc.
  - Now properly highlights types: int, void, string, vector, etc.
- Fix frontier pattern boundaries to include underscore (%W_ vs %W)

Testing:
✓ All 32 tests pass
✓ Space key now works in TUI for all major modes
✓ C++ keywords, types, strings, comments, and numbers now highlight correctly
✓ Lua mode unaffected
✓ GTK frontend unaffected

This makes the TUI fully usable for C++ development with proper syntax
highlighting and normal text editing capabilities.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Bernardo Magri 1 month ago
parent
commit
4ff0777db5
2 changed files with 43 additions and 25 deletions
  1. 35 21
      lua/major_modes/c_cpp_mode.lua
  2. 8 4
      src/tui_editor.cpp

+ 35 - 21
lua/major_modes/c_cpp_mode.lua

@@ -2,7 +2,7 @@
 
 -- Define helper function for escaping Lua patterns
 local function escape_pattern(text)
-    return text:gsub("([%%^%%$%ስ%%(%%)%%%.%%[%]%*%+%-%?])", "%%%%%1")
+    return text:gsub("([%%^%$%.%[%]%*%+%-%?%(%)])", "%%%1")
 end
 
 -- C/C++ Keywords
@@ -126,10 +126,10 @@ local function highlight_cpp_buffer()
         -- Strings (double and single quotes)
         local search_from = 1
         while search_from <= line_len do
-            -- Pattern for double-quoted strings (handles escaped quotes)
-            local dq_start_idx, dq_end_idx = string.find(line_text, '"[^"\\]*(?:\\.[^"\\]*)*"', search_from)
-            -- Pattern for single-quoted characters/strings (handles escaped quotes)
-            local sq_start_idx, sq_end_idx = string.find(line_text, "'[^'\\]*(?:\\.[^'\\]*)*'", search_from)
+            -- Pattern for double-quoted strings (simplified - handles basic cases)
+            local dq_start_idx, dq_end_idx = string.find(line_text, '"[^"]*"', search_from)
+            -- Pattern for single-quoted characters/strings (simplified)
+            local sq_start_idx, sq_end_idx = string.find(line_text, "'[^']*'", search_from)
             
             local current_match_start, current_match_end
             if dq_start_idx and (not sq_start_idx or dq_start_idx < sq_start_idx) then
@@ -149,8 +149,8 @@ local function highlight_cpp_buffer()
         end
 
         -- Numbers
-        -- This regex matches integers, decimals, and scientific notation, ensuring it's not part of a word.
-        for num_start_idx, num_end_idx in string.gmatch(line_text, "()%f[%D]%d+%.?%d*[eE]?[%+%-]?%d*%f[%D]()") do
+        -- Match integers and decimals (simplified pattern that works in Lua)
+        for num_start_idx, num_end_idx in string.gmatch(line_text, "()%d+%.?%d*()") do
             local range = lumacs.Range(
                 lumacs.Position(line_num, num_start_idx - 1),
                 lumacs.Position(line_num, num_end_idx - 1)
@@ -175,22 +175,36 @@ local function highlight_cpp_buffer()
             end
         end
 
-        -- Keywords
-        for kw_start_idx, kw_end_idx in string.gmatch(line_text, "()" .. full_keyword_pattern .. "()") do
-            local range = lumacs.Range(
-                lumacs.Position(line_num, kw_start_idx - 1),
-                lumacs.Position(line_num, kw_end_idx - 1)
-            )
-            buf:set_style(range, lumacs.TextAttribute(lumacs.ColorType.Keyword, 0))
+        -- Keywords - iterate through each keyword individually
+        for _, kw in ipairs(cpp_keywords) do
+            local search_pos = 1
+            while search_pos <= line_len do
+                local pattern = "%f[%w_]" .. escape_pattern(kw) .. "%f[%W_]"
+                local kw_start, kw_end = string.find(line_text, pattern, search_pos)
+                if not kw_start then break end
+                local range = lumacs.Range(
+                    lumacs.Position(line_num, kw_start - 1),
+                    lumacs.Position(line_num, kw_end)
+                )
+                buf:set_style(range, lumacs.TextAttribute(lumacs.ColorType.Keyword, 0))
+                search_pos = kw_end + 1
+            end
         end
 
-        -- Types
-        for type_start_idx, type_end_idx in string.gmatch(line_text, "()" .. full_type_pattern .. "()") do
-             local range = lumacs.Range(
-                lumacs.Position(line_num, type_start_idx - 1),
-                lumacs.Position(line_num, type_end_idx - 1)
-            )
-            buf:set_style(range, lumacs.TextAttribute(lumacs.ColorType.Type, 0))
+        -- Types - iterate through each type individually
+        for _, ty in ipairs(cpp_types) do
+            local search_pos = 1
+            while search_pos <= line_len do
+                local pattern = "%f[%w_]" .. escape_pattern(ty) .. "%f[%W_]"
+                local type_start, type_end = string.find(line_text, pattern, search_pos)
+                if not type_start then break end
+                local range = lumacs.Range(
+                    lumacs.Position(line_num, type_start - 1),
+                    lumacs.Position(line_num, type_end)
+                )
+                buf:set_style(range, lumacs.TextAttribute(lumacs.ColorType.Type, 0))
+                search_pos = type_end + 1
+            end
         end
 
         -- Operators (simple approach: highlight some common multi-char operators)

+ 8 - 4
src/tui_editor.cpp

@@ -363,13 +363,17 @@ bool TuiEditor::handle_input(int ch) {
         bool has_ctrl = key_name.find("C-") != std::string::npos;
         bool has_meta = key_name.find("M-") != std::string::npos;
 
-        if (!has_ctrl && !has_meta && key_name.length() == 1) {
+        // Special case: "Space" should be treated as a printable character
+        bool is_space = (key_name == "Space");
+        std::string char_to_insert = is_space ? " " : key_name;
+
+        if (!has_ctrl && !has_meta && (key_name.length() == 1 || is_space)) {
             // We can assume it's printable if length is 1 and it's not a special key (which resolve_key handles)
-            core_->command_system().execute("self-insert-command", {key_name});
-            
+            core_->command_system().execute("self-insert-command", {char_to_insert});
+
             // --- Macro Recording Logic for Self-Insert ---
             if (core_->is_recording_macro()) {
-                core_->record_key_sequence(key_name);
+                core_->record_key_sequence(char_to_insert);
             }
             // --------------------------------------------