Fix undo correctness, crash-recovery, and syntax highlighting bugs from full codebase review
Undo/redo: - cmd_newline recorded the undo node's position after the cursor moved past the split point, so undoing Enter joined the wrong pair of lines. - Backspace/Delete line-joins recorded UndoType::Newline, whose apply() semantics are inverted for a join (forward=split); add UndoType::JoinLines with correct forward/backward behavior. - Regex replace-all, indent/unindent region, kill-to-eol, kill-line, kill-region, and delete-word-prev/next mutated the buffer via the no-undo-recording raw APIs; they now record undo, grouped atomically via a shared UndoGroupGuard. - Plain-text replace-all with an empty replacement advanced the scan position one character too far, skipping adjacent/overlapping matches. Crash recovery / memory safety: - Buffer's move ctor/assignment never carried over swap_rec_, on_disk_identity_, or the visual-line-mode fields, so the crash-recovery journal silently stopped tracking a buffer whenever Editor's std::vector<Buffer> reallocated or shifted (e.g. opening/closing files). Added SwapManager::Rehome() plus Flush()-before-mutate in Editor::AddBuffer/CloseBuffer so the journal's Buffer* key and the background writer thread never reference a stale address. - UndoTree leaked its entire node graph (including all edit text) on every buffer close/reload; added ~UndoTree() to free it. - main.cc didn't restore the terminal if an exception escaped the run loop; added an RAII guard around Frontend::Shutdown(). Input handling: - Terminal frontend used getch() instead of get_wch(), silently dropping non-ASCII keyboard input despite linking wide-char ncurses. - KKeymap's C-k lookup switches on an already-lowercased key, making `case 'E'` unreachable dead code; C-k Shift-E silently aliased to C-k e. - ImGui file picker wasn't modal: keystrokes typed while it was open still reached the buffer underneath as edit commands, and Escape didn't close it. - Gated ImGuiInputHandler's unconditional fprintf/fflush diagnostics behind an IMGUI_IH_DEBUG macro, matching QtInputHandler's existing convention. Syntax highlighting: - Go/Rust/SQL highlighters weren't stateful, so multi-line /* */ comments mis-highlighted as code starting on the second line. - Python's triple-quoted-string handler didn't re-scan the remainder of a line after a string closed mid-line, missing a same-line reopen. - HighlighterEngine's state_last_contig_ field was declared but unused, forcing an O(n) scan of state_cache_ on every stateful lookup; wired it up as a real fast path and fixed InvalidateFrom() to keep it in sync. - kge's :syntax off was silently undone the next frame because apply_syntax_to_buffer() re-applied the config default unconditionally every frame; added a user-override flag mirroring the existing edit-mode-detection guard. Added regression tests for all of the above (test_undo.cc, test_kkeymap.cc, test_command_semantics.cc, test_search_replace_flow.cc, and a new test_syntax_highlighting.cc). Full suite (163 tests) passes, including under AddressSanitizer. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -9,6 +9,19 @@
|
||||
#include "KKeymap.h"
|
||||
#include "Editor.h"
|
||||
|
||||
// Verbose k-prefix suffix logging for debugging macOS/SDL key translation
|
||||
// issues. Default to off; enable by defining IMGUI_IH_DEBUG=1 at compile
|
||||
// time. Mirrors QtInputHandler.cc's QT_IH_DEBUG gate.
|
||||
#ifndef IMGUI_IH_DEBUG
|
||||
#define IMGUI_IH_DEBUG 0
|
||||
#endif
|
||||
|
||||
#if IMGUI_IH_DEBUG
|
||||
#define IH_LOGF(...) do { std::fprintf(stderr, __VA_ARGS__); std::fflush(stderr); } while (0)
|
||||
#else
|
||||
#define IH_LOGF(...) ((void) 0)
|
||||
#endif
|
||||
|
||||
|
||||
static bool
|
||||
map_key(const SDL_Keycode key,
|
||||
@@ -182,18 +195,19 @@ map_key(const SDL_Keycode key,
|
||||
k_ctrl_pending = false;
|
||||
CommandId id;
|
||||
bool mapped = KLookupKCommand(ascii_key, pass_ctrl, id);
|
||||
#if IMGUI_IH_DEBUG
|
||||
// Diagnostics for u/U
|
||||
if (lower == 'u') {
|
||||
char disp = (ascii_key >= 0x20 && ascii_key <= 0x7e)
|
||||
? static_cast<char>(ascii_key)
|
||||
: '?';
|
||||
std::fprintf(stderr,
|
||||
"[kge] k-prefix suffix: sym=%d mods=0x%x ascii=%d '%c' ctrl2=%d pass_ctrl=%d mapped=%d id=%d\n",
|
||||
static_cast<int>(key), static_cast<unsigned int>(mod), ascii_key, disp,
|
||||
ctrl2 ? 1 : 0, pass_ctrl ? 1 : 0, mapped ? 1 : 0,
|
||||
mapped ? static_cast<int>(id) : -1);
|
||||
std::fflush(stderr);
|
||||
IH_LOGF(
|
||||
"[kge] k-prefix suffix: sym=%d mods=0x%x ascii=%d '%c' ctrl2=%d pass_ctrl=%d mapped=%d id=%d\n",
|
||||
static_cast<int>(key), static_cast<unsigned int>(mod), ascii_key, disp,
|
||||
ctrl2 ? 1 : 0, pass_ctrl ? 1 : 0, mapped ? 1 : 0,
|
||||
mapped ? static_cast<int>(id) : -1);
|
||||
}
|
||||
#endif
|
||||
if (mapped) {
|
||||
out = {true, id, "", 0};
|
||||
if (ed)
|
||||
@@ -524,15 +538,17 @@ ImGuiInputHandler::ProcessSDLEvent(const SDL_Event &e)
|
||||
bool pass_ctrl = k_ctrl_pending_;
|
||||
k_ctrl_pending_ = false;
|
||||
bool mapped = KLookupKCommand(ascii_key, pass_ctrl, id);
|
||||
#if IMGUI_IH_DEBUG
|
||||
// Diagnostics: log any k-prefix TEXTINPUT suffix mapping
|
||||
char disp = (ascii_key >= 0x20 && ascii_key <= 0x7e)
|
||||
? static_cast<char>(ascii_key)
|
||||
: '?';
|
||||
std::fprintf(stderr,
|
||||
"[kge] k-prefix TEXTINPUT suffix: ascii=%d '%c' mapped=%d id=%d\n",
|
||||
ascii_key, disp, mapped ? 1 : 0,
|
||||
mapped ? static_cast<int>(id) : -1);
|
||||
std::fflush(stderr);
|
||||
{
|
||||
char disp = (ascii_key >= 0x20 && ascii_key <= 0x7e)
|
||||
? static_cast<char>(ascii_key)
|
||||
: '?';
|
||||
IH_LOGF("[kge] k-prefix TEXTINPUT suffix: ascii=%d '%c' mapped=%d id=%d\n",
|
||||
ascii_key, disp, mapped ? 1 : 0,
|
||||
mapped ? static_cast<int>(id) : -1);
|
||||
}
|
||||
#endif
|
||||
if (mapped) {
|
||||
mi = {true, id, "", 0};
|
||||
if (ed_)
|
||||
|
||||
Reference in New Issue
Block a user