Nimbin[12]?SDL & Graphics / sdl_runtime_compiler / commits / 4171af0

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 carriedline linting With Claude

Christian Immanuel · 2026-06-10 08:15 · 4171af0fd8ff21f221035b1ef5b661a766e29f9f

 src/levels/director.cpp        | 24 +++++++++------
 src/levels/director.hpp        |  3 +-
 src/states/game_state.cpp      | 70 ++++++++++--------------------------------
 src/states/menu_state.cpp      |  2 ++
 src/systems/carry_lex.cpp      | 70 ++++++++++++++++++++++++++++++++++++++++++
 src/systems/carry_lex.hpp      |  9 ++++++
 src/systems/word_ai_system.cpp | 17 +++++++---
 src/text/handler.cpp           | 10 ++----
 src/text/handler.hpp           |  4 +++
 src/text/mesh.cpp              |  2 +-
 src/text/token.cpp             |  2 +-
 src/utils/frame_profiler.hpp   | 10 +++---
 src/wall/wall.cpp              |  5 +++
 13 files changed, 146 insertions(+), 82 deletions(-)

diff --git a/src/levels/director.cpp b/src/levels/director.cpp
index dae0cf8..4e90f73 100644
--- a/src/levels/director.cpp
+++ b/src/levels/director.cpp
@@ -107,6 +107,7 @@ Director::setup_Scene(GameContext& ctx, const Scene::Data& scene)
                 t->ai.state  = Text::Token::Ai::State::Attack;
                 t->body.mode = PhysicsMode::AiHover;
                 t->set_Color(ConfigColor::attack);
+                t->set_ColorSource(Text::ColorSource::Static);
                 t->ai.state_timer = 0.0;
                 t->ai.phase_timer = 0.5;
                 break;
@@ -114,6 +115,7 @@ Director::setup_Scene(GameContext& ctx, const Scene::Data& scene)
                 t->ai.state  = Text::Token::Ai::State::Flee;
                 t->body.mode = PhysicsMode::AiHover;
                 t->set_Color(ConfigColor::flee);
+                t->set_ColorSource(Text::ColorSource::Static);
                 break;
             case Text::Token::Ai::State::Idle:
             case Text::Token::Ai::State::Stunned:
@@ -145,24 +147,28 @@ Director::teardown_Scene(GameContext& ctx)
 void
 Director::show_Details(GameContext& ctx) const
 {
-    SPIKE("direktor.show_Details");
     if (!ctx.info_wall) return;
     Text::Handler& info = *ctx.info_wall;
-    info.clear();
 
-    if (_runner->is_Complete()) return;
+    if (_runner->is_Complete()) { info.clear(); return; }
+
+    const Level::Step* step = &_runner->get_Current();
+    const size_t tag = std::hash<const void*>{}(step) | 1;
+    if (info.view_Is(tag)) return;
+
+    info.clear();
 
-    const Scene::Data* key = &_runner->get_Current().scene;
+    const Scene::Data* key = &step->scene;
     if (key != _detail_key) {
-        const std::string& details = _runner->get_Current().details;
-        _detail_lines = details.empty()
-                      ? DynArray<std::string>{}
-                      : Text::wrap_String(details, 92);
+        const std::string& details = step->details;
+        _detail_lines = details.empty() ? DynArray<std::string>{}
+                                        : Text::wrap_String(details, 92);
         _detail_key = key;
     }
-
     for (const std::string& line : _detail_lines)
         if (!line.empty()) info.append_Line(line);
+
+    info.set_View(tag);
 }
 
 
diff --git a/src/levels/director.hpp b/src/levels/director.hpp
index 7a9593b..ad883f3 100644
--- a/src/levels/director.hpp
+++ b/src/levels/director.hpp
@@ -58,7 +58,8 @@ class Director
     StepLedger             _ledger;
     DynArray<StepPlate>    _step_plates;
     DynArray<GameObject*>  _step_scenery;
-    mutable const Scene::Data*    _detail_key { nullptr };
+    mutable const Level::Step*    _detail_shown_for { nullptr };
+    mutable const Scene::Data*    _detail_key       { nullptr };
     mutable DynArray<std::string> _detail_lines;
 
     void spawn_Grants  (GameContext& ctx);
diff --git a/src/states/game_state.cpp b/src/states/game_state.cpp
index fbc5ec7..96482cc 100644
--- a/src/states/game_state.cpp
+++ b/src/states/game_state.cpp
@@ -9,6 +9,7 @@
 #include "game_state.hpp"
 #include "rendering/gl_renderer.hpp"
 #include "systems/aim.hpp"
+#include "systems/carry_lex.hpp"
 #include "systems/celebration_system.hpp"
 #include "systems/cpp_highlight_system.hpp"
 #include "systems/game_mod_abi.h"
@@ -22,8 +23,6 @@
 #include "wall/label.hpp"
 #include "wall/wall.hpp"
 
-static void lex_CarriedLine(CarriedLine& carry);
-
 GameState::GameState(size_t starting_level)
 
 {
@@ -217,7 +216,6 @@ GameState::on_Pick()
         _level_director-> record_PickedUp(base->text);
         base->set_Look       (ConfigColor::keep, Text::Token::depth_floor);
         Wall::show_TokenInfo (_info, base->text);
-        lex_CarriedLine      (_carried);
         carry_BeginAnim      (std::move(base), true);
         return true;
     }
@@ -230,7 +228,6 @@ GameState::on_Pick()
         _level_director->record_PickedUp(word->text);
         word->set_Look      (ConfigColor::keep, Text::Token::depth_floor);
         Wall::show_TokenInfo(_info, word->text);
-        lex_CarriedLine      (_carried);
         carry_BeginAnim     (std::move(word), false);
         return true;
     }
@@ -245,6 +242,7 @@ GameState::on_DropLast()
 
     Text::TokenPtr word = _carried.pop_back();
     if (!word) return;
+    revert_ToSingle(word.get());
 
     infowall_Clear();
 
@@ -272,6 +270,7 @@ GameState::on_ShootOne()
 
     Text::TokenPtr word = _carried.pop_back();
     if (!word) return false;
+    revert_ToSingle(word.get());
 
     infowall_Clear();
 
@@ -309,24 +308,22 @@ GameState::on_ShootAll()
 
     DynArray<Text::TokenPtr> all_words;
     for (CarriedWordNode* node : _carried.nodes())
-        if (node->word)
+        if (node->word) {
+            revert_ToSingle    (node->word.get());
             all_words.push_back(std::move(node->word));
+        }
 
     _carried = CarriedLine();
     Aim::Result aim;
     if (all_words.empty())
         return;
 
-    { FP_ZONE("dispatch.ShootAll.aim");
-        aim = Aim::crosshair_Target(*_ctx.camera, _ctx.words, _ctx.scenery,
-                                     _text_walls, World::get_BoxScale()); }
-    { FP_ZONE("dispatch.ShootAll.launch");
-        _projectiles_sys->launch(_projectiles, std::move(all_words),
-                                 _ctx.camera->fwd, _ctx.camera->pos, aim); }
-    { FP_ZONE("dispatch.ShootAll.details");
-        _level_director->show_Details(_ctx); }
-    { FP_ZONE("dispatch.ShootAll.record");
-        _level_director->record_Shot (); }
+    aim = Aim::crosshair_Target(*_ctx.camera, _ctx.words, _ctx.scenery,
+                                 _text_walls, World::get_BoxScale());
+    _projectiles_sys->launch(_projectiles, std::move(all_words),
+                             _ctx.camera->fwd, _ctx.camera->pos, aim);
+    _level_director->show_Details(_ctx);
+    _level_director->record_Shot ();
 }
 
 
@@ -683,6 +680,7 @@ GameState::update(double dt, const GlRenderer* gl)
         carriedline_Update();
 
         _word_anim_sys->update(_anims_add, _anims_drop, _carried, _ctx.words, dt);
+        lex_CarriedLine(_carried);
 
         _pult_sys->  update(dt, *_ctx.camera);
         _plate_sys-> update(dt, *_ctx.player, _level_director->get_StepPlates());
@@ -947,7 +945,8 @@ GameState::update_WorldSimulation(double dt)
                 word->ai.state       = Text::Token::Ai::State::Stunned;
                 word->ai.state_timer = 0.0;
                 word->body.vel       = Vec3{0, 0, 0};
-                word->set_Color(ConfigColor::stunned);
+                word->set_Color      (ConfigColor::stunned);
+                word->set_ColorSource(Text::ColorSource::Static);
             }
         }
     }
@@ -1116,6 +1115,7 @@ GameState::typing_ClearPreview()
     }
 
     _typing_words.clear();
+    lex_CarriedLine(_carried);
 }
 
 
@@ -1207,41 +1207,3 @@ GameState::on_EditorScroll(int8_t dt)
     if (dt > 0) _editor.scroll( ConfigWall::scroll_step);
     else        _editor.scroll(-ConfigWall::scroll_step);
 }
-
-
-
-
-void
-lex_CarriedLine(CarriedLine& carry)
-{
-    std::string src;
-    DynArray<Text::Token::Data*> toks;
-    for (CarriedWordNode* n : carry.nodes()) {
-        if (!n->word) continue;
-        Text::Token::Data* t = n->word.get();
-        if (!src.empty()) src += ' ';
-        t->byte_begin = src.size();
-        src += t->text;
-        t->byte_end   = src.size();
-        toks.push_back(t);
-    }
-    if (toks.empty()) return;
-
-    DynArray<Text::HiSpan> spans = Text::CppHighlight::classify(src);
-
-    for (Text::Token::Data* t : toks) {
-        size_t best = 0;
-        Text::Token::Type type = Text::Token::Type::Identifier;
-        bool found = false;
-        for (const Text::HiSpan& s : spans) {
-            size_t lo = (t->byte_begin > s.byte_begin) ? t->byte_begin : s.byte_begin;
-            size_t hi = (t->byte_end   < s.byte_end)   ? t->byte_end   : s.byte_end;
-            if (lo < hi && (hi - lo) > best) { best = hi - lo; type = s.type; found = true; }
-        }
-        t->set_ColorSource(Text::ColorSource::Lexed);
-        if (found) t->set_Context(Text::Token::get_Color(type));
-        else       t->clear_Context();
-    }
-}
-
-
diff --git a/src/states/menu_state.cpp b/src/states/menu_state.cpp
index c921d6e..7628d9d 100644
--- a/src/states/menu_state.cpp
+++ b/src/states/menu_state.cpp
@@ -119,6 +119,7 @@ MenuState::build_Items()
                                       _items[i].slot_pos,
                                       color);
         _items[i].text3d->body.mode = PhysicsMode::Static;
+        _items[i].text3d->set_ColorSource(Text::ColorSource::Static);
     }
 }
 
@@ -296,6 +297,7 @@ MenuState::update(double dt, const GlRenderer*)
     const size_t hover = pick_FromCrosshair();
     if (hover != static_cast<size_t>(-1) && hover != _selected) {
         _items[_selected].text3d->set_Color(COLOR_NORMAL);
+
         _items[hover].text3d->set_Color(COLOR_SELECTED);
         _selected = hover;
     }
diff --git a/src/systems/carry_lex.cpp b/src/systems/carry_lex.cpp
new file mode 100644
index 0000000..2147532
--- /dev/null
+++ b/src/systems/carry_lex.cpp
@@ -0,0 +1,70 @@
+#include "carry_lex.hpp"
+
+#include <SDL3/SDL.h>
+#include <string>
+
+#include "systems/carry_system.hpp"
+#include "text/text.hpp"
+#include "text/token.hpp"
+
+
+// Re-lex the entire carried line as ONE source string, then assign each word a
+// context colour by mapping highlight spans onto its byte range. Mirrors
+// Handler::apply_Highlight, but synchronous and scoped to a single joined line.
+void
+lex_CarriedLine(CarriedLine& carry)
+{
+    // 1) Build the joined source + per-token byte spans, in reading order.
+    //    NOTE: nodes() is front->back. If the carried line reads right-to-left
+    //    on screen (get_SlotPosition lays out from `last` with decreasing
+    //    offset), switch this to reverse_nodes() so "int x" lexes as "int x"
+    //    and not "x int". Verify against the on-screen order once.
+    std::string                   src;
+    DynArray<Text::Token::Data*>  toks;
+
+    for (CarriedWordNode* n : carry.nodes()) {
+        if (!n->word) continue;
+        Text::Token::Data* t = n->word.get();
+        if (!src.empty()) src += ' ';
+        t->byte_begin = src.size();
+        src += t->text;
+        t->byte_end   = src.size();
+        toks.push_back(t);
+    }
+    if (toks.empty()) return;
+
+    // 2) Classify the whole line as one context.
+    DynArray<Text::HiSpan> spans = Text::CppHighlight::classify(src);
+
+    // 3) Map spans -> tokens by maximum byte overlap.
+    for (Text::Token::Data* t : toks) {
+        size_t            best  = 0;
+        Text::Token::Type type  = Text::Token::Type::Identifier;
+        bool              found = false;
+
+        for (const Text::HiSpan& s : spans) {
+            size_t lo = (t->byte_begin > s.byte_begin) ? t->byte_begin : s.byte_begin;
+            size_t hi = (t->byte_end   < s.byte_end)   ? t->byte_end   : s.byte_end;
+            if (lo < hi) {
+                size_t ov = hi - lo;
+                if (ov > best) { best = ov; type = s.type; found = true; }
+            }
+        }
+
+        t->set_ColorSource(Text::ColorSource::Lexed);   // carried line is a context
+        if (found) t->set_Context(Text::Token::get_Color(type));
+        else       t->clear_Context();                  // no span -> single fallback
+    }
+}
+
+
+// A word that leaves the carried line is no longer in a context: drop the
+// context flag and select its lone (single) colour, the same colour it shows on
+// the floor or as a projectile.
+void
+revert_ToSingle(Text::Token::Data* word)
+{
+    if (!word) return;
+    word->clear_Context();
+    word->set_ColorSource(Text::ColorSource::Lint);
+}
diff --git a/src/systems/carry_lex.hpp b/src/systems/carry_lex.hpp
new file mode 100644
index 0000000..4dd36b7
--- /dev/null
+++ b/src/systems/carry_lex.hpp
@@ -0,0 +1,9 @@
+#pragma once
+
+
+struct CarriedLine;
+
+namespace Text { namespace Token { struct Data; } }
+
+void lex_CarriedLine (CarriedLine& carry);
+void revert_ToSingle (Text::Token::Data* word);
diff --git a/src/systems/word_ai_system.cpp b/src/systems/word_ai_system.cpp
index 8574e9c..67271cc 100644
--- a/src/systems/word_ai_system.cpp
+++ b/src/systems/word_ai_system.cpp
@@ -46,12 +46,14 @@ WordAiSystem::update(DynArray<Text::TokenPtr>& words,
                 if (!s) continue;
                 s->body.mode = PhysicsMode::Dynamic;
                 s->body.vel  = w->body.vel * 0.5;
-                s->set_Color(Text::Token::get_Color(Text::CppHighlight::classify_Word(s->text)));
+                s->recolor        ();
+                s->set_ColorSource(Text::ColorSource::Lint);
                 released.push_back(std::move(s));
             }
             w->ai.stolen.clear();
             w->ai.state = Text::Token::Ai::State::Idle;
-            w->set_Color(Text::Token::get_Color(Text::CppHighlight::classify_Word(w->text)));
+            w->recolor        ();
+            w->set_ColorSource(Text::ColorSource::Lint);
             continue;
         }
 
@@ -161,6 +163,7 @@ void WordAiSystem::update_Attack(Text::Token::Data& w, const Player& p, CarriedL
                     Text::TokenPtr stolen = std::move(node->word);
                     stolen->body.mode = PhysicsMode::AiHover;
                     stolen->set_Color(ConfigColor::attack);
+                    stolen->set_ColorSource(Text::ColorSource::Static);
                     w.ai.stolen.push_back(std::move(stolen));
                     enter_Flee(w);
                     carried.compact();
@@ -269,7 +272,9 @@ WordAiSystem::release_StolenWords(Text::Token::Data& w, DynArray<Text::TokenPtr>
         if (!s) continue;
         s->body.mode = PhysicsMode::Dynamic;
         s->body.vel  = { w.body.vel.x * 0.5, 2.0, w.body.vel.z * 0.5 };
-        s->set_Color(Text::Token::get_Color(Text::CppHighlight::classify_Word(s->text)));
+        s->recolor        ();
+        s->set_ColorSource(Text::ColorSource::Lint);
+
         out.push_back(std::move(s));
     }
     w.ai.stolen.clear();
@@ -280,6 +285,7 @@ void
 WordAiSystem::enter_Attack(Text::Token::Data& w)
 {
     w.set_Color(ConfigColor::attack);
+    w.set_ColorSource(Text::ColorSource::Static);
     w.body.mode      = PhysicsMode::AiHover;
     w.ai.state       = Text::Token::Ai::State::Attack;
     w.ai.state_timer = 0.0;
@@ -297,6 +303,7 @@ void
 WordAiSystem::enter_Flee(Text::Token::Data& w)
 {
     w.set_Color(ConfigColor::flee);
+    w.set_ColorSource(Text::ColorSource::Static);
     w.body.mode      = PhysicsMode::AiHover;
     w.ai.state       = Text::Token::Ai::State::Flee;
     w.ai.state_timer = 0.0;
@@ -313,6 +320,7 @@ WordAiSystem::enter_Stunned(Text::Token::Data& w)
     w.ai.state_timer = 0.0;
     w.body.vel       = Vec3{};
     w.set_Color(ConfigColor::stunned);
+    w.set_ColorSource(Text::ColorSource::Static);
 }
 
 
@@ -323,7 +331,8 @@ WordAiSystem::enter_Idle(Text::Token::Data& w)
     w.ai.state_timer = 0.0;
     w.body.mode      = PhysicsMode::Hover;
     w.body.hover_height = w.ai.hover_height_saved;
-    w.set_Color(Text::Token::get_Color(Text::CppHighlight::classify_Word(w.text)));
+    w.recolor();
+    w.set_ColorSource(Text::ColorSource::Lint);
 }
 
 
diff --git a/src/text/handler.cpp b/src/text/handler.cpp
index 8fa461d..9ee98e3 100644
--- a/src/text/handler.cpp
+++ b/src/text/handler.cpp
@@ -103,13 +103,13 @@ Handler::set_DefaultStyle(float depth, float scale, double line_height, double w
 }
 
 
-void
-Handler::clear()
+void Handler::clear()
 {
     _lines     .clear();
     _last_src  .clear();
     _label_off .clear();
     _label_text.clear();
+    _view_tag = 0;
     mark_Dirty();
 }
 
@@ -144,8 +144,6 @@ Handler::set_Lines(const DynArray<std::string>& lines)
 void
 Handler::append_Line(const std::string& text)
 {
-    SPIKE("append_Line");
-
     for (Line& l : _lines) {
         l.y += _line_height;
         for (TokenPtr& t : l.tokens) if (t) t->pos.y += _line_height;
@@ -168,8 +166,6 @@ Handler::append_Line(const std::string& text)
 void
 Handler::append_Tokens(DynArray<TokenPtr> block)
 {
-    SPIKE("append_Tokens");
-
     DynArray<TokenPtr> toks;
     toks.reserve(block.size());
     for (TokenPtr& t : block) if (t) toks.push_back(std::move(t));
@@ -213,7 +209,6 @@ Handler::append_Tokens(DynArray<TokenPtr> block)
 TokenPtr
 Handler::detach(Token::Data* t)
 {
-    SPIKE("detach");
     if (edit_Active()) return nullptr;
 
     for (size_t li = 0; li < _lines.size(); ++li) {
@@ -236,7 +231,6 @@ Handler::detach(Token::Data* t)
 void
 Handler::attach(DynArray<TokenPtr> tokens, const Vec3& impact)
 {
-    SPIKE("attach");
     if (tokens.empty()) return;
     if (_layout == LayoutMode::Mono)
          attach_Mono   (tokens, impact);
diff --git a/src/text/handler.hpp b/src/text/handler.hpp
index 9001b48..7658791 100644
--- a/src/text/handler.hpp
+++ b/src/text/handler.hpp
@@ -114,6 +114,8 @@ public:
     void set_Style     (SDL_FColor color, float depth, float scale);
     void set_Collidable(bool on);
     void kick          (size_t word_index, const Vec3& projectile_vel, const Vec3& normal);
+    bool view_Is       (size_t tag) const { return tag != 0 && tag == _view_tag; }
+    void set_View      (size_t tag)       { _view_tag = tag; }
     bool is_Empty      () const { return _lines.empty() || _lines[0].tokens.empty(); }
     const DynArray<TokenPtr>&
          get_Words     () const;
@@ -136,6 +138,8 @@ private:
 
     DynArray<std::string> _status;
 
+    size_t _view_tag { 0 };          // identity of currently-materialised info-wall content; 0 = none
+
     bool _lex_enabled  { true };
     bool _show_numbers { false };
     bool _both_columns { false };
diff --git a/src/text/mesh.cpp b/src/text/mesh.cpp
index fc54790..0a09299 100644
--- a/src/text/mesh.cpp
+++ b/src/text/mesh.cpp
@@ -359,7 +359,7 @@ bool
 TextMesh::build(const std::string& font_path,
                 const std::string& text, int size, float /*depth*/, float scale)
 {
-    SPIKE("mesh.build");
+    // SPIKE("mesh.build");
     verts.clear(); tris.clear(); is_front.clear(); vertex_normals.clear();
 
     FT_Face face = acquire_Face(font_path, size);
diff --git a/src/text/token.cpp b/src/text/token.cpp
index a0bb79b..47084d1 100644
--- a/src/text/token.cpp
+++ b/src/text/token.cpp
@@ -188,7 +188,7 @@ Data::get_Distance(Vec3 position) const
 void
 Data::rebuild_Mesh()
 {
-    SPIKE("rebuild_mesh");
+    // SPIKE("rebuild_mesh");
     loaded = mesh.build(font_path, text, 64, depth, scale);
     if (!loaded) {
         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
diff --git a/src/utils/frame_profiler.hpp b/src/utils/frame_profiler.hpp
index e362eb4..bade110 100644
--- a/src/utils/frame_profiler.hpp
+++ b/src/utils/frame_profiler.hpp
@@ -30,10 +30,12 @@
 #include <cstdio>
 
 #ifdef FRAME_PROFILER_DISABLED
-#  define FP_ZONE(name)   ((void)0)
-#  define FRAME_BEGIN()   ((void)0)
-#  define FRAME_END()     ((void)0)
-#  define FRAME_SORT(s)   ((void)0)
+#  define FP_ZONE(name)  ((void)0)
+#  define FRAME_BEGIN()  ((void)0)
+#  define FRAME_END()    ((void)0)
+#  define FRAME_SORT_PEAK     ((void)0)
+#  define FRAME_SORT_TOTAL    ((void)0)
+#  define FRAME_SORT_AVERAGE  ((void)0)
 #else
 
 
diff --git a/src/wall/wall.cpp b/src/wall/wall.cpp
index d5373f5..e7b0696 100644
--- a/src/wall/wall.cpp
+++ b/src/wall/wall.cpp
@@ -110,6 +110,9 @@ namespace Wall
     void
     show_TokenInfo(::Text::Handler& info, const std::string& token_text)
     {
+        const size_t tag = std::hash<std::string>{}(token_text) | 1;
+        if (info.view_Is(tag)) return;
+
         info.clear();
 
         Text::Token::Type tt = Text::CppHighlight::classify_Word(token_text);
@@ -121,6 +124,8 @@ namespace Wall
 
         for (const std::string& line : { line1, line2, line3 })
             if (!line.empty()) info.append_Line(line);
+
+        info.set_View(tag);
     }