Nimbin[12]?SDL & Graphics / sdl_runtime_compiler / commits / ed09b9a

sdl_runtime_compiler git · main

SDL3 game for running and compiling code at runtime

sdl3 c++ compiler dlopen cmake · first commit 2026-04-19 · last commit 2026-07-03 (3 months ago) · synced 3 days ago · upstream: git.ide3.de/hsnr/sdl-runtime-compiler

C++ 72.3% C 26.2%
git clone https://git.christianimmanuel.de/sdl-graphics/sdl_runtime_compiler.gitwget https://git.christianimmanuel.de/sdl-graphics/sdl_runtime_compiler/archive/sdl_runtime_compiler.tar.gz

Fix vim count tokens as words And add meta command labels With Claude

Christian Immanuel · 2026-06-28 07:31 · ed09b9a07176125eb52f77e58d33949f11861e28

 src/text/vim_buffer.cpp | 88 ++++++++++++++++++++++++++++++++++++++++---------
 1 file changed, 72 insertions(+), 16 deletions(-)

diff --git a/src/text/vim_buffer.cpp b/src/text/vim_buffer.cpp
index ec4ad20..6a387aa 100644
--- a/src/text/vim_buffer.cpp
+++ b/src/text/vim_buffer.cpp
@@ -52,8 +52,17 @@ void VimBuffer::key_escape()
     if (_mode == Mode::Command)
         { cancel_Command(); return; }
 
+    // <Esc> cancels any half-typed normal-mode command — a pending count ("12")
+    // and/or operator ("d", "c", "y", "g", ">", "<") — so it disappears from the
+    // showcmd, matching vim. The cursor only nudges left when actually leaving
+    // insert mode, not when cancelling a count in normal mode.
+    const bool was_insert = (_mode == Mode::Insert);
+
     _mode = Mode::Normal;
-    if (_col > 0) --_col;
+    _count = 0;
+    _pending.clear();
+
+    if (was_insert && _col > 0) --_col;
     clamp_Col(); touch();
 }
 
@@ -388,8 +397,46 @@ VimBuffer::is_Dirty() const
 { return _dirty; }
 
 
+// ── word motion treats each TOKEN as a vim word ──────────────────────────────
+// The classes mirror the game tokenizer (handler.cpp relayout/chunker): a word
+// run [A-Za-z0-9_], an operator run from the op set, single-char punctuation,
+// or a whitespace run. So dw/cw/yw/w/e/b stop on token boundaries exactly where
+// the rendered tokens split — e.g. dw on "fuu(buu)" with the cursor on 'f'
+// deletes just "fuu".
+namespace {
+
+enum CharClass : uint8_t { CC_SPACE, CC_WORD, CC_OP, CC_PUNCT };
+
+inline CharClass
+char_Class(char ch)
+{
+    if (ch == ' ' || ch == '\t')                       return CC_SPACE;
+    if ((ch >= 'A' && ch <= 'Z') ||
+        (ch >= 'a' && ch <= 'z') ||
+        (ch >= '0' && ch <= '9') || ch == '_')         return CC_WORD;
+    for (const char* o = "+-*/%=<>!&|^~.:?"; *o; ++o)   // op set
+        if (ch == *o)                                  return CC_OP;
+    return CC_PUNCT;
+}
+
+// Exclusive end of the token that starts at p: word/op/space form runs, a punct
+// char is a token of its own (matching how the renderer splits tokens).
+inline int
+token_End(const std::string& s, int p)
+{
+    const int n = static_cast<int>(s.size());
+    if (p >= n) return p;
+    const CharClass c = char_Class(s[static_cast<size_t>(p)]);
+    if (c == CC_PUNCT) return p + 1;
+    while (p < n && char_Class(s[static_cast<size_t>(p)]) == c) ++p;
+    return p;
+}
+
+} // namespace
+
+
 int
-VimBuffer::word_EndCol(int times) const
+VimBuffer::word_EndCol(int times) const                  // cw: exclusive end of token(s)
 {
     const std::string&
               line = _lines[static_cast<size_t>(_row)];
@@ -397,14 +444,14 @@ VimBuffer::word_EndCol(int times) const
           int c    = _col;
 
     for (int k = 0; k < times; ++k) {
-        while (c < n && line[static_cast<size_t>(c)] == ' ') ++c;   // skip spaces
-        while (c < n && line[static_cast<size_t>(c)] != ' ') ++c;   // to end of word
+        while (c < n && char_Class(line[static_cast<size_t>(c)]) == CC_SPACE) ++c;
+        if (c < n) c = token_End(line, c);
     }
     return c;
 }
 
 int
-VimBuffer::next_WordCol(int times) const
+VimBuffer::next_WordCol(int times) const                 // w: start of the next token
 {
     const std::string&
               line = _lines[static_cast<size_t>(_row)];
@@ -412,8 +459,10 @@ VimBuffer::next_WordCol(int times) const
           int c    = _col;
 
     for (int k = 0; k < times; ++k) {
-        while (c < n && line[static_cast<size_t>(c)] != ' ') ++c;   // skip current word
-        while (c < n && line[static_cast<size_t>(c)] == ' ') ++c;   // skip spaces to next word
+        if (c >= n) break;
+        if (char_Class(line[static_cast<size_t>(c)]) != CC_SPACE)
+            c = token_End(line, c);                                       // past this token
+        while (c < n && char_Class(line[static_cast<size_t>(c)]) == CC_SPACE) ++c;
     }
     return c;
 }
@@ -423,7 +472,7 @@ VimBuffer::next_WordCol(int times) const
 void
 VimBuffer::yank_Lines(int n)
 {
-    const int end = (_row + n < static_cast<int>(_lines.size())) 
+    const int end = (_row + n < static_cast<int>(_lines.size()))
                   ? _row + n
                   : static_cast<int>(_lines.size());
 
@@ -585,7 +634,7 @@ VimBuffer::indent_Lines(int n, int dir)
 }
 
 
-// ── e: last char of the n-th word forward (always advances at least one) ─────
+// ── e: last char of the n-th token forward (always advances at least one) ────
 int
 VimBuffer::end_WordCol(int times) const
 {
@@ -594,24 +643,31 @@ VimBuffer::end_WordCol(int times) const
     if (n == 0) return 0;
     int c = _col;
     for (int k = 0; k < times; ++k) {
-        if (c < n) ++c; // step off current char
-        while (c < n     && line[static_cast<size_t>(c)]     == ' ') ++c; // skip spaces
-        while (c + 1 < n && line[static_cast<size_t>(c + 1)] != ' ') ++c; // to last char of word
+        ++c;                                                              // step off current char
+        while (c < n && char_Class(line[static_cast<size_t>(c)]) == CC_SPACE) ++c;
+        if (c < n) {
+            const int e = token_End(line, c);                            // exclusive end
+            c = (e > c) ? e - 1 : c;                                      // → last char of token
+        }
     }
     return (c < n) ? c : n - 1;
 }
 
 
-// ── b: start of the n-th word backward (always retreats at least one) ────────
+// ── b: start of the n-th token backward (always retreats at least one) ───────
 int
 VimBuffer::prev_WordCol(int times) const
 {
     const std::string& line = _lines[static_cast<size_t>(_row)];
     int c = _col;
     for (int k = 0; k < times; ++k) {
-        if (c > 0) --c; // step off current char
-        while (c > 0 && line[static_cast<size_t>(c)]     == ' ') --c;   // skip spaces backward
-        while (c > 0 && line[static_cast<size_t>(c - 1)] != ' ') --c;   // to first char of word
+        --c;                                                              // step off current char
+        while (c >= 0 && char_Class(line[static_cast<size_t>(c)]) == CC_SPACE) --c;
+        if (c < 0) break;
+        const CharClass cl = char_Class(line[static_cast<size_t>(c)]);
+        if (cl != CC_PUNCT)                                               // word/op run → its start
+            while (c - 1 >= 0 && char_Class(line[static_cast<size_t>(c - 1)]) == cl) --c;
+        // CC_PUNCT is a single-char token: c already sits at its start
     }
     return (c > 0) ? c : 0;
 }