Kaynağa Gözat

refactor: Implement logging system with spdlog

- Add spdlog dependency via FetchContent in CMakeLists.txt.
- Create Logger wrapper in include/lumacs/logger.hpp and src/logger.cpp.
- Replace std::cerr calls with spdlog calls across the codebase (main, plugins, buffers, editor core, commands, window manager, macros, lua api).
- Update PLAN.md to mark Phase 3 as completed.
Bernardo Magri 1 ay önce
ebeveyn
işleme
6871637981

+ 10 - 0
CMakeLists.txt

@@ -43,6 +43,14 @@ FetchContent_Declare(
 )
 FetchContent_MakeAvailable(sol2)
 
+# spdlog
+FetchContent_Declare(
+    spdlog
+    GIT_REPOSITORY https://github.com/gabime/spdlog.git
+    GIT_TAG v1.12.0
+)
+FetchContent_MakeAvailable(spdlog)
+
 # Google Test
 FetchContent_Declare(
     googletest
@@ -77,6 +85,7 @@ add_library(lumacs_core STATIC
     src/macro_manager.cpp
     src/rectangle_manager.cpp
     src/plugin_manager.cpp
+    src/logger.cpp
 )
 
 target_include_directories(lumacs_core PUBLIC
@@ -87,6 +96,7 @@ target_include_directories(lumacs_core PUBLIC
 
 target_link_libraries(lumacs_core PUBLIC
     sol2::sol2
+    spdlog::spdlog
     ${LUA_LIBRARIES}
     ${CURSES_LIBRARIES}
 )

+ 8 - 4
documentation/PLAN.md

@@ -93,12 +93,15 @@ Lumacs/
     *   ✅ Increase test coverage for existing components, especially CommandSystem, and Window. (LuaApi coverage expanded and fixed)
     *   ✅ Implement integration tests to verify the interactions between the modularized components and the overall editor behavior.
 
-### Phase 3: Logging and Observability
+### Phase 3: Logging and Observability ✅ Completed
 
-*   **Subtask 3.1: Integrate a C++ Logging Library:**
+*   **Subtask 3.1: Integrate a C++ Logging Library:** ✅ Completed
     *   **Recommendation:** spdlog or loguru. Integrate the chosen library into the CMake build system.
-*   **Subtask 3.2: Replace `std::cerr` Calls:**
+    *   Included `spdlog` via FetchContent in `CMakeLists.txt`.
+    *   Created `Logger` wrapper class in `include/lumacs/logger.hpp` and `src/logger.cpp`.
+*   **Subtask 3.2: Replace `std::cerr` Calls:** ✅ Completed
     *   Replace all instances of `std::cerr` for debug/error output with appropriate calls to the new logging library.
+    *   Updated `src/main.cpp`, `src/plugin_manager.cpp`, `src/buffer_manager.cpp`, `src/gtk_editor.cpp`, `src/editor_core.cpp`, `src/command_system.cpp`, `src/window_manager.cpp`, `src/macro_manager.cpp`, and `src/lua_api.cpp`.
     *   Define different log levels (e.g., DEBUG, INFO, WARN, ERROR) and configure log sinks (e.g., console, file).
 
 ### Phase 4: Dependency Management
@@ -165,7 +168,8 @@ This phase aimed to enhance the Command System to support robust, type-safe, and
 
 - [x] Expand test coverage for existing components: `LuaApi`, `CommandSystem`, and `Window`.
 - [x] Implement integration tests to verify component interactions.
-- [ ] Phase 3: Logging and Observability (Integrate spdlog, replace std::cerr).
+- [x] Phase 3: Logging and Observability (Integrate spdlog, replace std::cerr).
+- [ ] Phase 4: Dependency Management (Review `sol2` dependency).
 
 ## Current State Summary
 

+ 14 - 0
include/lumacs/logger.hpp

@@ -0,0 +1,14 @@
+#pragma once
+
+#include <memory>
+#include <spdlog/spdlog.h>
+
+namespace lumacs {
+
+class Logger {
+public:
+    static void init();
+    static std::shared_ptr<spdlog::logger> get();
+};
+
+} // namespace lumacs

+ 4 - 3
src/buffer_manager.cpp

@@ -1,7 +1,8 @@
 #include "lumacs/buffer_manager.hpp"
 #include "lumacs/editor_core.hpp" // For EditorCore access
+#include "lumacs/logger.hpp"
+#include <spdlog/spdlog.h>
 #include <algorithm>
-#include <iostream> // TODO: Replace with proper logging
 
 namespace lumacs {
 
@@ -29,7 +30,7 @@ void BufferManager::new_buffer(std::string name) {
     } else {
         // This case should ideally not happen if EditorCore initializes properly
         // For robustness, create a default window if none exists
-        std::cerr << "[ERROR] No active window to set new buffer. This should not happen." << std::endl;
+        spdlog::error("No active window to set new buffer. This should not happen.");
     }
 
     core_.emit_event(EditorEvent::BufferModified);
@@ -113,7 +114,7 @@ bool BufferManager::close_buffer(const std::string& name) {
                 win->set_buffer(*other_buf_it);
             } else {
                 // This scenario should be prevented by the buffers_.size() <= 1 check
-                std::cerr << "[ERROR] No other buffer found to switch to after closing." << std::endl;
+                spdlog::error("No other buffer found to switch to after closing.");
                 return false; 
             }
         }

+ 5 - 5
src/command_system.cpp

@@ -1,8 +1,9 @@
 #include "lumacs/command_system.hpp"
-#include <iostream> // TODO: Replace with proper logging as per PLAN.md Phase 3
 #include "lumacs/editor_core.hpp" // Now included here for full definition
 #include "lumacs/minibuffer_manager.hpp" // Added for interactive argument gathering
 #include "lumacs/i_command_target.hpp" // Added for ICommandTarget
+#include "lumacs/logger.hpp"
+#include <spdlog/spdlog.h>
 #include <algorithm> // For std::sort
 #include <vector>    // For std::vector
 #include <string_view> // For std::string_view
@@ -22,9 +23,9 @@ std::optional<int> CommandContext::get_int_arg(size_t index) const {
         try {
             return std::stoi(args_[index]);
         } catch (const std::invalid_argument& e) {
-            std::cerr << "CommandContext::get_int_arg: Invalid argument for stoi: " << args_[index] << std::endl;
+            spdlog::error("CommandContext::get_int_arg: Invalid argument for stoi: {}", args_[index]);
         } catch (const std::out_of_range& e) {
-            std::cerr << "CommandContext::get_int_arg: Out of range for stoi: " << args_[index] << std::endl;
+            spdlog::error("CommandContext::get_int_arg: Out of range for stoi: {}", args_[index]);
         }
     }
     return std::nullopt;
@@ -61,8 +62,7 @@ void CommandSystem::register_command(const std::string& name, CommandFunction fu
         if (commands_.find(alias) == commands_.end()) {
             alias_map_[alias] = name;
         } else {
-            std::cerr << "[WARNING] Alias '" << alias << "' for command '" << name 
-                      << "' conflicts with existing canonical command. Alias ignored." << std::endl;
+            spdlog::warn("Alias '{}' for command '{}' conflicts with existing canonical command. Alias ignored.", alias, name);
         }
     }
 }

+ 8 - 7
src/editor_core.cpp

@@ -10,8 +10,9 @@
 #include "lumacs/register_manager.hpp" // New include for RegisterManager
 #include "lumacs/macro_manager.hpp" // New include for MacroManager
 #include "lumacs/rectangle_manager.hpp" // New include for RectangleManager
+#include "lumacs/logger.hpp"
+#include <spdlog/spdlog.h>
 #include <algorithm>
-#include <iostream>
 
 namespace lumacs {
 
@@ -374,7 +375,7 @@ void EditorCore::kill_region() {
         emit_event(EditorEvent::BufferModified);
         emit_event(EditorEvent::CursorMoved);
 
-        std::cerr << "[DEBUG] Killed region: '" << killed_text << "'" << std::endl;
+        spdlog::debug("Killed region: '{}'", killed_text);
     }
 }
 
@@ -392,7 +393,7 @@ void EditorCore::kill_word() {
         kill_ring_manager_->push(text); // Delegate to manager
         buf.erase(range);
         emit_event(EditorEvent::BufferModified);
-        std::cerr << "[DEBUG] Killed word: '" << text << "'" << std::endl;
+        spdlog::debug("Killed word: '{}'", text);
     }
 }
 
@@ -412,7 +413,7 @@ void EditorCore::backward_kill_word() {
         window_manager_->active_window()->set_cursor(start_pos);
         emit_event(EditorEvent::BufferModified);
         emit_event(EditorEvent::CursorMoved);
-        std::cerr << "[DEBUG] Backward killed word: '" << text << "'" << std::endl;
+        spdlog::debug("Backward killed word: '{}'", text);
     }
 }
 
@@ -433,7 +434,7 @@ void EditorCore::copy_region_as_kill() {
         buf.deactivate_mark();
 
         set_message("Region copied");
-        std::cerr << "[DEBUG] Copied region: '" << copied_text << "'" << std::endl;
+        spdlog::debug("Copied region: '{}'", copied_text);
     }
 }
 
@@ -479,7 +480,7 @@ void EditorCore::yank() {
     emit_event(EditorEvent::BufferModified);
     emit_event(EditorEvent::CursorMoved);
 
-    std::cerr << "[DEBUG] Yanked: '" << text << "'" << std::endl;
+    spdlog::debug("Yanked: '{}'", text);
 }
 
 void EditorCore::yank_pop() {
@@ -526,7 +527,7 @@ void EditorCore::yank_pop() {
     emit_event(EditorEvent::BufferModified);
     emit_event(EditorEvent::CursorMoved);
 
-    std::cerr << "[DEBUG] Yank-pop: '" << text << "'" << std::endl;
+    spdlog::debug("Yank-pop: '{}'", text);
 }
 
 // === Registers (Delegated to RegisterManager) ===

+ 3 - 2
src/gtk_editor.cpp

@@ -12,7 +12,8 @@
 #include "lumacs/register_manager.hpp" // Include for RegisterManager
 #include "lumacs/macro_manager.hpp" // Include for MacroManager
 #include "lumacs/rectangle_manager.hpp" // Include for RectangleManager
-#include <iostream>
+#include "lumacs/logger.hpp"
+#include <spdlog/spdlog.h>
 #include <filesystem>
 #include <vector>
 #include <functional> // For std::function
@@ -642,7 +643,7 @@ std::unique_ptr<IEditorView> create_gtk_editor() {
 
 namespace lumacs {
 std::unique_ptr<IEditorView> create_gtk_editor() {
-    std::cerr << "Error: Lumacs was built without GTK support." << std::endl;
+    spdlog::error("Error: Lumacs was built without GTK support.");
     return nullptr;
 }
 } // namespace lumacs

+ 0 - 1
src/keybinding.cpp

@@ -3,7 +3,6 @@
 #include <sstream>
 #include <algorithm>
 #include <cctype>
-#include <iostream> // For std::cerr debugging
 
 namespace lumacs {
 

+ 38 - 0
src/logger.cpp

@@ -0,0 +1,38 @@
+#include "lumacs/logger.hpp"
+#include <spdlog/sinks/stdout_color_sinks.h>
+#include <spdlog/sinks/basic_file_sink.h>
+#include <vector>
+
+namespace lumacs {
+
+void Logger::init() {
+    // Check if already initialized
+    if (spdlog::get("lumacs")) {
+        return;
+    }
+
+    // Create console sink
+    auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
+    console_sink->set_level(spdlog::level::debug);
+    console_sink->set_pattern("[%H:%M:%S.%e] [%^%l%$] %v");
+
+    // Create file sink
+    auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>("lumacs_debug.log", true);
+    file_sink->set_level(spdlog::level::trace);
+
+    // Combine sinks
+    std::vector<spdlog::sink_ptr> sinks {console_sink, file_sink};
+    auto logger = std::make_shared<spdlog::logger>("lumacs", sinks.begin(), sinks.end());
+    
+    logger->set_level(spdlog::level::debug);
+    logger->flush_on(spdlog::level::debug);
+
+    spdlog::register_logger(logger);
+    spdlog::set_default_logger(logger);
+}
+
+std::shared_ptr<spdlog::logger> Logger::get() {
+    return spdlog::get("lumacs");
+}
+
+} // namespace lumacs

+ 34 - 30
src/lua_api.cpp

@@ -4,7 +4,9 @@
 #include "lumacs/completion_system.hpp" // Added for CompletionSystem access
 #include "lumacs/plugin_manager.hpp" // Added for PluginManager access
 #include "lumacs/buffer_manager.hpp" // Added for BufferManager::BufferInfo
-#include <iostream>
+#include "lumacs/logger.hpp"
+#include <spdlog/spdlog.h>
+#include <iostream> // Kept for now if needed by sol2 internals or other parts, but trying to remove direct usage
 #include <fstream>
 #include <filesystem> // For std::filesystem::path
 
@@ -72,16 +74,16 @@ void LuaApi::set_core(EditorCore& core) {
 
 bool LuaApi::load_file(const std::filesystem::path& path) {
     try {
-        std::cerr << "[DEBUG] Loading Lua file: " << path << std::endl;
+        spdlog::debug("Loading Lua file: {}", path.string());
         lua_.script_file(path.string());
-        std::cerr << "[DEBUG] Lua file loaded successfully" << std::endl;
-        std::cout << "Loaded Lua file: " << path << std::endl;
+        spdlog::debug("Lua file loaded successfully");
+        spdlog::info("Loaded Lua file: {}", path.string());
         return true;
     } catch (const sol::error& e) {
-        std::cerr << "[ERROR] Lua error loading " << path << ": " << e.what() << std::endl;
+        spdlog::error("Lua error loading {}: {}", path.string(), e.what());
         return false;
     } catch (const std::exception& e) {
-        std::cerr << "[ERROR] Exception loading " << path << ": " << e.what() << std::endl;
+        spdlog::error("Exception loading {}: {}", path.string(), e.what());
         return false;
     }
 }
@@ -91,7 +93,7 @@ bool LuaApi::execute(std::string_view code) {
         lua_.script(code);
         return true;
     } catch (const sol::error& e) {
-        std::cerr << "Lua error: " << e.what() << std::endl;
+        spdlog::error("Lua error: {}", e.what());
         return false;
     }
 }
@@ -110,17 +112,17 @@ bool LuaApi::load_init_file() {
 
     for (const auto& path : search_paths) {
         if (std::filesystem::exists(path)) {
-            std::cout << "Found init file: " << path << std::endl;
+            spdlog::info("Found init file: {}", path.string());
             return load_file(path);
         }
     }
 
-    std::cout << "No init.lua found (searched: ./init.lua, ~/.config/lumacs/init.lua, ~/.lumacs/init.lua)" << std::endl;
+    spdlog::warn("No init.lua found (searched: ./init.lua, ~/.config/lumacs/init.lua, ~/.lumacs/init.lua)");
     return false;
 }
 
 void LuaApi::bind_key(std::string key, sol::function callback, std::string description) {
-    std::cerr << "[DEBUG] Registering key binding: " << key << std::endl;
+    spdlog::debug("Registering key binding: {}", key);
     
     // Create a unique command name for the Lua function
     // This is a simple way; a more robust system might handle conflicts or namespaces
@@ -192,7 +194,7 @@ bool LuaApi::execute_key_binding(const std::string& key) {
         it->second();
         return true;
     } catch (const sol::error& e) {
-        std::cerr << "Lua error executing key binding '" << key << "': " << e.what() << "\n";
+        spdlog::error("Lua error executing key binding '{}': {}", key, e.what());
         return false;
     }
 }
@@ -200,7 +202,7 @@ bool LuaApi::execute_key_binding(const std::string& key) {
 // Manual C functions implementations
 int LuaApi::get_active_buffer_line_count_lua() {
     if (!core_ || !core_->active_window() || !core_->active_window()->buffer_ptr()) {
-        std::cerr << "[DEBUG_MANUAL] get_active_buffer_line_count_lua: Core, active window or buffer is null!\n";
+        spdlog::warn("get_active_buffer_line_count_lua: Core, active window or buffer is null!");
         return 0;
     }
     return static_cast<int>(core_->buffer().line_count());
@@ -208,7 +210,7 @@ int LuaApi::get_active_buffer_line_count_lua() {
 
 void LuaApi::lua_editor_move_right() {
     if (!core_) {
-        std::cerr << "[DEBUG_MANUAL] lua_editor_move_right: Core is null!\n";
+        spdlog::warn("lua_editor_move_right: Core is null!");
         return;
     }
     core_->move_right();
@@ -216,7 +218,7 @@ void LuaApi::lua_editor_move_right() {
 
 void LuaApi::lua_editor_new_buffer(const std::string& name) {
     if (!core_) {
-        std::cerr << "[DEBUG_MANUAL] lua_editor_new_buffer: Core is null!\n";
+        spdlog::warn("lua_editor_new_buffer: Core is null!");
         return;
     }
     core_->new_buffer(name);
@@ -224,7 +226,7 @@ void LuaApi::lua_editor_new_buffer(const std::string& name) {
 
 sol::optional<std::string> LuaApi::lua_editor_get_buffer_by_name(const std::string& name) {
     if (!core_) {
-        std::cerr << "[DEBUG_MANUAL] lua_editor_get_buffer_by_name: Core is null!\n";
+        spdlog::warn("lua_editor_get_buffer_by_name: Core is null!");
         return sol::nullopt;
     }
     if (auto buffer = core_->get_buffer_by_name(name)) {
@@ -235,7 +237,7 @@ sol::optional<std::string> LuaApi::lua_editor_get_buffer_by_name(const std::stri
 
 void LuaApi::lua_editor_set_message(const std::string& message) {
     if (!core_) {
-        std::cerr << "[DEBUG_MANUAL] lua_editor_set_message: Core is null!\n";
+        spdlog::warn("lua_editor_set_message: Core is null!");
         return;
     }
     core_->set_message(message);
@@ -243,7 +245,7 @@ void LuaApi::lua_editor_set_message(const std::string& message) {
 
 void LuaApi::lua_config_set_string(const std::string& key, const std::string& value) {
     if (!core_) {
-        std::cerr << "[DEBUG_MANUAL] lua_config_set_string: Core is null!\n";
+        spdlog::warn("lua_config_set_string: Core is null!");
         return;
     }
     core_->config().set(key, value);
@@ -251,7 +253,7 @@ void LuaApi::lua_config_set_string(const std::string& key, const std::string& va
 
 std::string LuaApi::lua_config_get_string(const std::string& key, const std::string& default_val) {
     if (!core_) {
-        std::cerr << "[DEBUG_MANUAL] lua_config_get_string: Core is null!\n";
+        spdlog::warn("lua_config_get_string: Core is null!");
         return default_val;
     }
     return core_->config().get<std::string>(key, default_val);
@@ -259,7 +261,7 @@ std::string LuaApi::lua_config_get_string(const std::string& key, const std::str
 
 void LuaApi::lua_config_set_bool(const std::string& key, bool value) {
     if (!core_) {
-        std::cerr << "[DEBUG_MANUAL] lua_config_set_bool: Core is null!\n";
+        spdlog::warn("lua_config_set_bool: Core is null!");
         return;
     }
     core_->config().set(key, value);
@@ -267,7 +269,7 @@ void LuaApi::lua_config_set_bool(const std::string& key, bool value) {
 
 bool LuaApi::lua_config_get_bool(const std::string& key, bool default_val) {
     if (!core_) {
-        std::cerr << "[DEBUG_MANUAL] lua_config_get_bool: Core is null!\n";
+        spdlog::warn("lua_config_get_bool: Core is null!");
         return default_val;
     }
     return core_->config().get<bool>(key, default_val);
@@ -275,7 +277,7 @@ bool LuaApi::lua_config_get_bool(const std::string& key, bool default_val) {
 
 void LuaApi::lua_config_set_int(const std::string& key, int value) {
     if (!core_) {
-        std::cerr << "[DEBUG_MANUAL] lua_config_set_int: Core is null!\n";
+        spdlog::warn("lua_config_set_int: Core is null!");
         return;
     }
     core_->config().set(key, value);
@@ -283,7 +285,7 @@ void LuaApi::lua_config_set_int(const std::string& key, int value) {
 
 int LuaApi::lua_config_get_int(const std::string& key, int default_val) {
     if (!core_) {
-        std::cerr << "[DEBUG_MANUAL] lua_config_get_int: Core is null!\n";
+        spdlog::warn("lua_config_get_int: Core is null!");
         return default_val;
     }
     return core_->config().get<int>(key, default_val);
@@ -326,7 +328,7 @@ void LuaApi::setup_api() {
 
     // Initialize MobDebug if enabled
     if (core_->config().get<bool>("debug_lua", false)) { // Corrected Config access
-        std::cerr << "[DEBUG] Lua debugging enabled." << std::endl;
+        spdlog::debug("Lua debugging enabled.");
         std::string debug_host = core_->config().get<std::string>("debug_host", "127.0.0.1"); // Corrected Config access
         int debug_port = core_->config().get<int>("debug_port", 8171); // Corrected Config access
 
@@ -335,19 +337,19 @@ void LuaApi::setup_api() {
             // Explicitly require 'mobdebug' module
             sol::optional<sol::table> mobdebug = lua_["require"]("mobdebug");
             if (mobdebug) {
-                std::cerr << "[DEBUG] MobDebug module loaded." << std::endl;
+                spdlog::debug("MobDebug module loaded.");
                 sol::function mobdebug_start = mobdebug.value()["start"];
                 if (mobdebug_start.valid()) {
                     mobdebug_start(debug_host, debug_port);
-                    std::cerr << "[INFO] MobDebug started on " << debug_host << ":" << debug_port << std::endl;
+                    spdlog::info("MobDebug started on {}:{}", debug_host, debug_port);
                 } else {
-                    std::cerr << "[ERROR] MobDebug.start function not found." << std::endl;
+                    spdlog::error("MobDebug.start function not found.");
                 }
             } else {
-                std::cerr << "[ERROR] Failed to load MobDebug module. Is LuaRocks installed and configured?" << std::endl;
+                spdlog::error("Failed to load MobDebug module. Is LuaRocks installed and configured?");
             }
         } catch (const sol::error& e) {
-            std::cerr << "[ERROR] Lua error during MobDebug initialization: " << e.what() << std::endl;
+            spdlog::error("Lua error during MobDebug initialization: {}", e.what());
         }
     }
 }
@@ -761,10 +763,12 @@ void LuaApi::register_functions() {
 
     // Print function that goes to stderr (since stdout is used by TUI)
     lua_["print"] = [](sol::variadic_args args) {
+        std::string message;
         for (auto arg : args) {
-            std::cerr << lua_tostring(arg.lua_state(), arg.stack_index()) << "\t";
+            message += lua_tostring(arg.lua_state(), arg.stack_index());
+            message += "\t";
         }
-        std::cerr << std::endl;
+        spdlog::info("[Lua] {}", message);
     };
 
     // Command system functions

+ 3 - 2
src/macro_manager.cpp

@@ -1,6 +1,7 @@
 #include "lumacs/macro_manager.hpp"
 #include "lumacs/editor_core.hpp" // For EditorCore access
-#include <iostream> // For debug output, consider replacing with logging
+#include "lumacs/logger.hpp"
+#include <spdlog/spdlog.h>
 
 namespace lumacs {
 
@@ -46,7 +47,7 @@ void MacroManager::end_kbd_macro_or_call() {
             // TODO: Execute the actual key binding for this key
             // This would require MacroManager to have access to core_->keybinding_manager()
             core_.keybinding_manager().process_key(key); // Replay the key sequence
-            // std::cerr << "[MACRO] Would execute key: " << key << std::endl;
+            spdlog::debug("[MACRO] Executing key: {}", key);
         }
     }
 }

+ 9 - 2
src/main.cpp

@@ -1,10 +1,12 @@
 #include "lumacs/editor_core.hpp"
 #include "lumacs/tui_editor.hpp"
 #include "lumacs/gtk_editor.hpp"
+#include "lumacs/logger.hpp"
 #include <iostream>
 #include <string>
 #include <vector>
 #include <filesystem>
+#include <spdlog/spdlog.h>
 
 using namespace lumacs;
 
@@ -16,6 +18,10 @@ void print_help() {
 }
 
 int main(int argc, char* argv[]) {
+    // Initialize Logger
+    Logger::init();
+    spdlog::info("Lumacs starting up...");
+
     bool force_tui = false;
     std::string filename;
 
@@ -53,12 +59,13 @@ int main(int argc, char* argv[]) {
             if (!force_tui && !view) {
                 // If we tried to load GTK and failed (e.g. create_gtk_editor returned null), warn user?
                 // For now, create_gtk_editor prints to stderr if built without GTK.
+                spdlog::warn("GTK frontend requested but not available/loaded. Falling back to TUI.");
             }
             view = create_tui_editor();
         }
 
         if (!view) {
-            std::cerr << "Failed to create editor interface." << std::endl;
+            spdlog::critical("Failed to create editor interface.");
             return 1;
         }
 
@@ -83,7 +90,7 @@ int main(int argc, char* argv[]) {
         core.clear_event_callbacks();
 
     } catch (const std::exception& e) {
-        std::cerr << "Fatal Error: " << e.what() << std::endl;
+        spdlog::critical("Fatal Error: {}", e.what());
         return 1;
     }
 

+ 16 - 13
src/plugin_manager.cpp

@@ -1,7 +1,10 @@
 #include "lumacs/plugin_manager.hpp"
 #include "lumacs/editor_core.hpp"
 #include "lumacs/lua_api.hpp"
-#include <iostream>
+#include "lumacs/logger.hpp"
+#include <spdlog/spdlog.h>
+#include <filesystem>
+#include <algorithm>
 
 namespace lumacs {
 
@@ -13,7 +16,7 @@ void PluginManager::discover_plugins(const std::vector<std::filesystem::path>& p
     discovered_plugins_.clear();
     for (const auto& dir : plugin_dirs) {
         if (!std::filesystem::exists(dir) || !std::filesystem::is_directory(dir)) {
-            std::cerr << "[WARNING] Plugin directory not found or not a directory: " << dir << std::endl;
+            spdlog::warn("Plugin directory not found or not a directory: {}", dir.string());
             continue;
         }
 
@@ -21,7 +24,7 @@ void PluginManager::discover_plugins(const std::vector<std::filesystem::path>& p
             if (entry.is_regular_file() && entry.path().extension() == ".lua") {
                 std::string plugin_name = entry.path().stem().string();
                 discovered_plugins_[plugin_name] = entry.path();
-                std::cerr << "[DEBUG] Discovered plugin: " << plugin_name << " at " << entry.path() << std::endl;
+                spdlog::debug("Discovered plugin: {} at {}", plugin_name, entry.path().string());
             }
         }
     }
@@ -30,18 +33,18 @@ void PluginManager::discover_plugins(const std::vector<std::filesystem::path>& p
 bool PluginManager::load_plugin(const std::string& name) {
     auto it = discovered_plugins_.find(name);
     if (it == discovered_plugins_.end()) {
-        std::cerr << "[ERROR] Plugin not found: " << name << std::endl;
+        spdlog::error("Plugin not found: {}", name);
         return false;
     }
 
     if (loaded_plugins_.count(name)) {
-        std::cerr << "[WARNING] Plugin already loaded: " << name << std::endl;
+        spdlog::warn("Plugin already loaded: {}", name);
         return true; // Already loaded, consider it a success
     }
 
     // Load the Lua file
     if (!lua_api_.load_file(it->second)) {
-        std::cerr << "[ERROR] Failed to load Lua script for plugin: " << name << std::endl;
+        spdlog::error("Failed to load Lua script for plugin: {}", name);
         return false;
     }
 
@@ -52,14 +55,14 @@ bool PluginManager::load_plugin(const std::string& name) {
     
     execute_on_load(new_plugin); // Execute on_load function if defined in plugin
 
-    std::cerr << "[INFO] Plugin loaded: " << name << std::endl;
+    spdlog::info("Plugin loaded: {}", name);
     return true;
 }
 
 bool PluginManager::unload_plugin(const std::string& name) {
     auto it = loaded_plugins_.find(name);
     if (it == loaded_plugins_.end()) {
-        std::cerr << "[WARNING] Plugin not loaded: " << name << std::endl;
+        spdlog::warn("Plugin not loaded: {}", name);
         return false;
     }
 
@@ -70,7 +73,7 @@ bool PluginManager::unload_plugin(const std::string& name) {
     // its functions/variables if not explicitly cleared by the plugin's unload function.
     loaded_plugins_.erase(it);
 
-    std::cerr << "[INFO] Plugin unloaded: " << name << std::endl;
+    spdlog::info("Plugin unloaded: {}", name);
     return true;
 }
 
@@ -99,11 +102,11 @@ void PluginManager::execute_on_load(const Plugin& plugin) {
             sol::protected_function on_load = plugin_table["on_load"];
             if (on_load.valid()) {
                 on_load();
-                std::cerr << "[DEBUG] Executed on_load for plugin: " << plugin.name << std::endl;
+                spdlog::debug("Executed on_load for plugin: {}", plugin.name);
             }
         }
     } catch (const sol::error& e) {
-        std::cerr << "[ERROR] Lua error in plugin on_load(" << plugin.name << "): " << e.what() << std::endl;
+        spdlog::error("Lua error in plugin on_load({}): {}", plugin.name, e.what());
     }
 }
 
@@ -116,11 +119,11 @@ void PluginManager::execute_on_unload(const Plugin& plugin) {
             sol::protected_function on_unload = plugin_table["on_unload"];
             if (on_unload.valid()) {
                 on_unload();
-                std::cerr << "[DEBUG] Executed on_unload for plugin: " << plugin.name << std::endl;
+                spdlog::debug("Executed on_unload for plugin: {}", plugin.name);
             }
         }
     } catch (const sol::error& e) {
-        std::cerr << "[ERROR] Lua error in plugin on_unload(" << plugin.name << "): " << e.what() << std::endl;
+        spdlog::error("Lua error in plugin on_unload({}): {}", plugin.name, e.what());
     }
 }
 

+ 10 - 9
src/window_manager.cpp

@@ -1,7 +1,8 @@
 #include "lumacs/window_manager.hpp"
 #include "lumacs/editor_core.hpp" // For EditorCore access
+#include "lumacs/logger.hpp"
+#include <spdlog/spdlog.h>
 #include <algorithm>
-#include <iostream> // TODO: Replace with proper logging
 
 namespace lumacs {
 
@@ -98,7 +99,7 @@ bool WindowManager::set_active_window(std::shared_ptr<Window> window) {
 }
 
 void WindowManager::split_horizontally() {
-    std::cerr << "[DEBUG] split_horizontally() called" << std::endl;
+    spdlog::debug("split_horizontally() called");
 
     // New window sharing same buffer as active window
     auto new_window = std::make_shared<Window>(active_window_->buffer_ptr());
@@ -115,20 +116,20 @@ void WindowManager::split_horizontally() {
     );
 
     if (root_node_->type == LayoutNode::Type::Leaf && root_node_->window == active_window_) {
-        std::cerr << "[DEBUG] Replacing root node" << std::endl;
+        spdlog::debug("Replacing root node");
         root_node_ = split;
     } else {
-        std::cerr << "[DEBUG] Replacing window node in tree" << std::endl;
+        spdlog::debug("Replacing window node in tree");
         replace_window_node(root_node_, active_window_, split);
     }
 
     active_window_ = new_window; // Focus new window
     core_.emit_event(EditorEvent::WindowLayoutChanged);
-    std::cerr << "[DEBUG] split_horizontally() completed" << std::endl;
+    spdlog::debug("split_horizontally() completed");
 }
 
 void WindowManager::split_vertically() {
-    std::cerr << "[DEBUG] split_vertically() called" << std::endl;
+    spdlog::debug("split_vertically() called");
 
     // New window sharing same buffer as active window
     auto new_window = std::make_shared<Window>(active_window_->buffer_ptr());
@@ -146,16 +147,16 @@ void WindowManager::split_vertically() {
     );
 
     if (root_node_->type == LayoutNode::Type::Leaf && root_node_->window == active_window_) {
-        std::cerr << "[DEBUG] Replacing root node" << std::endl;
+        spdlog::debug("Replacing root node");
         root_node_ = split;
     } else {
-        std::cerr << "[DEBUG] Replacing window node in tree" << std::endl;
+        spdlog::debug("Replacing window node in tree");
         replace_window_node(root_node_, active_window_, split);
     }
     
     active_window_ = new_window;
     core_.emit_event(EditorEvent::WindowLayoutChanged);
-    std::cerr << "[DEBUG] split_vertically() completed" << std::endl;
+    spdlog::debug("split_vertically() completed");
 }
 
 void WindowManager::close_active_window() {