1+ #pragma once
2+
3+ #include < atomic>
4+ #include < chrono>
5+ #include < condition_variable>
6+ #include < deque>
7+ #include < filesystem>
8+ #include < functional>
9+ #include < mutex>
10+ #include < set>
11+ #include < string>
12+ #include < unordered_map>
13+ #include < vector>
14+
15+ #include < File/Macros.hpp>
16+ #include < TextEditor.h>
17+
18+ struct lua_State ;
19+ struct lua_Debug ;
20+
21+ namespace RC ::LuaMadeSimple
22+ {
23+ class Lua ;
24+ }
25+
26+ namespace RC ::GUI
27+ {
28+ // Represents a single entry in the Lua call stack
29+ struct LuaCallStackEntry
30+ {
31+ std::string function_name;
32+ std::string source;
33+ int line_number{0 };
34+ int current_line{0 };
35+ std::string what; // "Lua", "C", "main", "tail"
36+ std::string name_what; // "global", "local", "method", "field", ""
37+ };
38+
39+ // Represents a single stack slot value
40+ struct LuaStackSlot
41+ {
42+ int index{0 };
43+ std::string type_name; // "nil", "boolean", "number", "string", "table", "function", "userdata", "thread", "lightuserdata"
44+ std::string value_preview; // Short string representation of the value
45+ bool is_table{false };
46+ bool is_userdata{false };
47+ };
48+
49+ // Represents a local variable at a stack frame
50+ struct LuaLocalVariable
51+ {
52+ std::string name;
53+ std::string type_name;
54+ std::string value_preview;
55+ bool is_table{false };
56+ bool is_userdata{false };
57+ };
58+
59+ // Represents a stack frame with its local variables
60+ struct LuaStackFrame
61+ {
62+ std::string function_name;
63+ std::string source;
64+ int line_number{0 };
65+ int current_line{0 };
66+ std::string what;
67+ std::vector<LuaLocalVariable> locals;
68+ };
69+
70+ // Represents a value in the variable tree (for table inspection)
71+ struct LuaValueNode
72+ {
73+ std::string key;
74+ std::string type_name;
75+ std::string value_preview;
76+ bool is_expandable{false }; // true for tables/userdata with metatable
77+ bool is_expanded{false };
78+ std::vector<LuaValueNode> children;
79+ int depth{0 };
80+ std::string path; // Full path for fetching (e.g., "myTable.subTable.value")
81+ };
82+
83+ // Represents a breakpoint
84+ struct LuaBreakpoint
85+ {
86+ std::string source_file; // Source file path
87+ int line{0 };
88+ bool enabled{true };
89+ std::string condition; // Optional condition expression
90+ int hit_count{0 };
91+ };
92+
93+ // Represents a script file for viewing
94+ struct LuaScriptFile
95+ {
96+ std::string path;
97+ std::string content;
98+ std::vector<std::string> lines;
99+ bool loaded{false };
100+ };
101+
102+ // REPL history entry
103+ struct ReplHistoryEntry
104+ {
105+ std::string input;
106+ std::string output;
107+ bool is_error{false };
108+ std::chrono::system_clock::time_point timestamp;
109+ };
110+
111+ // Represents a captured Lua error with full context
112+ struct LuaErrorRecord
113+ {
114+ std::chrono::system_clock::time_point timestamp;
115+ std::string mod_name;
116+ std::string error_message;
117+ std::string traceback;
118+ std::vector<LuaCallStackEntry> call_stack;
119+ std::vector<LuaStackSlot> stack_snapshot;
120+ };
121+
122+ // Information about a tracked Lua state
123+ struct LuaStateInfo
124+ {
125+ lua_State* lua_state{nullptr };
126+ std::string mod_name;
127+ std::string state_type; // "main", "hook", "async"
128+ int current_stack_size{0 };
129+ std::vector<LuaStackSlot> current_stack;
130+ bool is_active{true };
131+ };
132+
133+ class LuaDebugger
134+ {
135+ public:
136+ static constexpr size_t MAX_ERROR_HISTORY = 100 ;
137+ static constexpr size_t MAX_STACK_PREVIEW_LENGTH = 64 ;
138+ static constexpr size_t MAX_REPL_HISTORY = 100 ;
139+ static constexpr size_t MAX_TABLE_DEPTH = 10 ;
140+
141+ private:
142+ // All tracked Lua states
143+ std::unordered_map<lua_State*, LuaStateInfo> m_lua_states;
144+ mutable std::mutex m_states_mutex;
145+
146+ std::deque<LuaErrorRecord> m_error_history;
147+ mutable std::mutex m_errors_mutex;
148+
149+ // UI state
150+ bool m_auto_scroll_errors{true };
151+ int m_selected_error_index{-1 };
152+ lua_State* m_selected_state{nullptr };
153+ bool m_show_stack_details{true };
154+ bool m_pause_on_error{false };
155+ std::string m_error_filter;
156+
157+ static LuaDebugger* s_instance;
158+
159+ // Breakpoints
160+ std::vector<LuaBreakpoint> m_breakpoints;
161+ mutable std::mutex m_breakpoints_mutex;
162+ std::atomic<bool > m_is_paused{false };
163+ std::atomic<bool > m_step_requested{false };
164+ std::atomic<bool > m_step_over_requested{false };
165+ std::atomic<bool > m_step_out_requested{false };
166+ std::atomic<bool > m_continue_requested{false };
167+ lua_State* m_paused_state{nullptr };
168+ int m_paused_line{0 };
169+ std::string m_paused_source;
170+ std::condition_variable m_pause_cv;
171+ std::mutex m_pause_mutex;
172+ int m_step_start_depth{0 };
173+
174+ // Cached paused state info (captured on Lua thread, read on GUI thread)
175+ // IMPORTANT: Never manipulate the Lua state from the GUI thread while paused!
176+ std::vector<LuaStackSlot> m_paused_stack_slots;
177+ std::vector<LuaCallStackEntry> m_paused_call_stack;
178+ std::vector<LuaStackFrame> m_paused_stack_frames; // Call stack with local variables
179+ mutable std::mutex m_paused_data_mutex;
180+
181+ // Track which states have debug hooks installed
182+ std::set<lua_State*> m_states_with_hooks;
183+
184+ // Script viewer (for Debug view)
185+ std::unordered_map<std::string, LuaScriptFile> m_script_cache;
186+ std::string m_current_script_path;
187+ int m_script_scroll_to_line{-1 };
188+ mutable std::mutex m_scripts_mutex;
189+
190+ // Script editor state (for Script Editor tab)
191+ TextEditor m_script_editor;
192+ bool m_script_is_dirty{false };
193+ std::string m_script_edit_path;
194+ std::string m_script_original_content;
195+
196+ // Track which states had debug enabled (for auto-restore on reload)
197+ std::set<std::string> m_debug_enabled_mods;
198+
199+ // Cached loaded module paths per state (populated on Lua thread)
200+ std::unordered_map<lua_State*, std::vector<std::string>> m_loaded_modules_cache;
201+ mutable std::mutex m_loaded_modules_mutex;
202+
203+ // REPL
204+ std::string m_repl_input;
205+ std::deque<ReplHistoryEntry> m_repl_history;
206+ int m_repl_history_index{-1 };
207+ std::vector<std::string> m_repl_input_history;
208+ mutable std::mutex m_repl_mutex;
209+ bool m_repl_pending{false };
210+ std::string m_repl_pending_result;
211+ bool m_repl_pending_is_error{false };
212+
213+ // Table inspection
214+ std::vector<LuaValueNode> m_globals_tree;
215+ std::set<std::string> m_expanded_paths;
216+ bool m_tree_refresh_requested{false };
217+
218+ // Stack frame expansion state (-1 = no change, 0 = collapse all, 1 = expand all)
219+ int m_stack_frames_expand_action{-1 };
220+
221+ public:
222+ LuaDebugger ();
223+ ~LuaDebugger ();
224+
225+ static auto get () -> LuaDebugger&;
226+ static auto has_instance () -> bool;
227+
228+ // State tracking
229+ auto register_lua_state (lua_State* L, const std::string& mod_name, const std::string& state_type) -> void;
230+ auto unregister_lua_state (lua_State* L) -> void;
231+ auto update_state_stack (lua_State* L) -> void;
232+
233+ // Error recording
234+ auto record_error (lua_State* L, const std::string& error_message, const std::string& traceback) -> void;
235+ auto clear_error_history () -> void;
236+ auto get_error_count () const -> size_t;
237+
238+ // Stack inspection utilities
239+ static auto get_stack_slots (lua_State* L) -> std::vector<LuaStackSlot>;
240+ static auto get_call_stack (lua_State* L) -> std::vector<LuaCallStackEntry>;
241+ static auto get_stack_frames_with_locals (lua_State* L) -> std::vector<LuaStackFrame>;
242+ static auto format_stack_value (lua_State* L, int index, size_t max_length = MAX_STACK_PREVIEW_LENGTH) -> std::string;
243+ static auto get_enhanced_traceback (lua_State* L, const std::string& message, int level = 0 ) -> std::string;
244+ static auto get_globals (lua_State* L, size_t max_entries = 100 ) -> std::vector<std::pair<std::string, LuaStackSlot>>;
245+
246+ // Breakpoint management
247+ auto add_breakpoint (const std::string& source, int line, const std::string& condition = " " ) -> void;
248+ auto remove_breakpoint (const std::string& source, int line) -> void;
249+ auto toggle_breakpoint (const std::string& source, int line) -> void;
250+ auto clear_all_breakpoints () -> void;
251+ auto has_breakpoint (const std::string& source, int line) const -> bool;
252+ auto is_paused () const -> bool { return m_is_paused.load (); }
253+
254+ // Debug control
255+ auto continue_execution () -> void;
256+ auto step_into () -> void;
257+ auto step_over () -> void;
258+ auto step_out () -> void;
259+
260+ // Debug hook (called from Lua)
261+ static auto debug_hook (lua_State* L, lua_Debug* ar) -> void;
262+ auto install_debug_hook (lua_State* L) -> void;
263+ auto uninstall_debug_hook (lua_State* L) -> void;
264+ auto has_debug_hook (lua_State* L) const -> bool;
265+
266+ // Script loading
267+ auto load_script (const std::string& path) -> const LuaScriptFile*;
268+ auto get_mod_scripts (lua_State* L) -> std::vector<std::string>;
269+ auto save_script (const std::string& path, const std::string& content) -> bool;
270+ auto reload_mod_for_state (lua_State* L) -> void;
271+
272+ // REPL
273+ auto execute_repl (lua_State* L, const std::string& code) -> void;
274+
275+ // Table inspection
276+ static auto get_table_children (lua_State* L, const std::string& path, int depth = 0 ) -> std::vector<LuaValueNode>;
277+ auto request_table_expand (const std::string& path) -> void;
278+
279+ // GUI rendering
280+ auto render () -> void;
281+
282+ private:
283+ auto render_state_list () -> void;
284+ auto render_stack_view () -> void;
285+ auto render_globals_view () -> void;
286+ auto render_error_log () -> void;
287+ auto render_error_details (const LuaErrorRecord& error) -> void;
288+ auto render_call_stack (const std::vector<LuaCallStackEntry>& call_stack) -> void;
289+ auto render_controls () -> void;
290+ auto render_debug_view () -> void;
291+ auto render_script_editor () -> void;
292+ auto render_mods_tab () -> void;
293+ auto render_breakpoints_panel () -> void;
294+ auto render_repl () -> void;
295+ auto render_value_tree (std::vector<LuaValueNode>& nodes) -> void;
296+ auto render_debug_controls () -> void;
297+
298+ auto find_mod_name_for_state (lua_State* L) const -> std::string;
299+ auto request_globals_refresh () -> void;
300+ auto request_loaded_modules_refresh () -> void;
301+ auto check_breakpoint (lua_State* L, lua_Debug* ar) -> bool;
302+ auto wait_for_continue () -> void;
303+ auto get_call_depth (lua_State* L) -> int;
304+
305+ // Filter for globals view
306+ std::string m_globals_filter;
307+
308+ // Cached globals (since we can't access Lua from GUI thread directly)
309+ std::vector<std::pair<std::string, LuaStackSlot>> m_cached_globals;
310+ lua_State* m_cached_globals_state{nullptr };
311+ bool m_globals_refresh_requested{false };
312+ mutable std::mutex m_globals_mutex;
313+
314+ // Tree view support for globals
315+ std::unordered_map<std::string, std::vector<std::pair<std::string, LuaStackSlot>>> m_globals_children_cache;
316+ std::set<std::string> m_pending_table_expansions;
317+
318+ // Helper for recursive tree rendering
319+ auto render_globals_tree_node (const std::vector<std::pair<std::string, LuaStackSlot>>& children, const std::string& parent_path, int depth) -> void;
320+
321+ // Helper to get table entries at a given path
322+ static auto get_table_entries_at_path (lua_State* L, const std::string& path) -> std::vector<std::pair<std::string, LuaStackSlot>>;
323+
324+ // Mod management
325+ struct ModInfo
326+ {
327+ std::string name;
328+ std::filesystem::path path;
329+ bool enabled_via_txt{false };
330+ bool enabled_via_mods_txt{false };
331+ bool is_running{false };
332+
333+ bool is_enabled () const { return enabled_via_txt || enabled_via_mods_txt; }
334+ };
335+
336+ std::vector<ModInfo> m_discovered_mods;
337+ bool m_mods_list_dirty{true };
338+ std::string m_new_mod_name;
339+ bool m_show_create_mod_popup{false };
340+ std::string m_new_file_name;
341+ bool m_show_create_file_popup{false };
342+ std::filesystem::path m_create_file_mod_path;
343+ int m_pending_editor_tab_switch{-1 };
344+
345+ auto refresh_mods_list () -> void;
346+ auto create_new_mod (const std::string& name) -> bool;
347+ auto create_new_file (const std::string& mod_path, const std::string& filename) -> bool;
348+ auto set_mod_enabled (const std::filesystem::path& mod_path, bool enabled) -> void;
349+ };
350+
351+ } // namespace RC::GUI
0 commit comments