Просмотр исходного кода

fix(core): handle non-existent files gracefully

Updated BufferManager::load_file to prevent crashes when checking file equivalence for non-existent paths. Added logic to create new buffers for non-existent files, allowing for file creation via opening. Extended Buffer class with set_file_path method.
Bernardo Magri 1 месяц назад
Родитель
Сommit
11c960fcf5
4 измененных файлов с 53 добавлено и 9 удалено
  1. 1 0
      documentation/PLAN.md
  2. 3 0
      include/lumacs/buffer.hpp
  3. 8 0
      src/buffer.cpp
  4. 41 9
      src/buffer_manager.cpp

+ 1 - 0
documentation/PLAN.md

@@ -144,6 +144,7 @@ Lumacs/
     - [x] **File Path Completion**: Implemented robust file path autocompletion with tilde expansion and directory browsing.
     - [x] **GTK Minibuffer Cursor**: Fixed cursor rendering to be a proper block cursor positioned correctly at the editing point, not a line at the end of the text.
     - [x] **TUI Cursor**: Replaced software block cursor with standard hardware cursor (`curs_set(2)`). Added fallback software cursor with forced `A_REVERSE` to ensure visibility on all terminals.
+    - [x] **File Loading Robustness**: Fixed crash on non-existent files and added support for creating new files via open command.
 - ✅ **Theme System Refactoring**:
     - [x] Implemented `editor:create_and_register_theme` Lua API to allow theme definition from Lua.
     - [x] Factored all hardcoded C++ themes (`default`, `everforest-dark`, `dracula`, `solarized-dark`, `nord`, `gruvbox-light`) into individual Lua files (`lua/themes/*.lua`).

+ 3 - 0
include/lumacs/buffer.hpp

@@ -211,6 +211,9 @@ public:
     /// @brief Get the file path associated with this buffer (if any).
     [[nodiscard]] std::optional<std::filesystem::path> file_path() const noexcept;
 
+    /// @brief Set the file path associated with this buffer.
+    void set_file_path(const std::filesystem::path& path);
+
     // === Buffer Properties ===
 
     /// @brief Get the buffer name.

+ 8 - 0
src/buffer.cpp

@@ -339,6 +339,14 @@ std::optional<std::filesystem::path> Buffer::file_path() const noexcept {
     return file_path_;
 }
 
+void Buffer::set_file_path(const std::filesystem::path& path) {
+    file_path_ = path;
+    name_ = path.filename().string();
+    // Re-detect language based on new path
+    language_ = detect_language(path);
+    emit_event(BufferEvent::LanguageChanged);
+}
+
 // === Buffer Properties ===
 
 const std::string& Buffer::name() const noexcept {

+ 41 - 9
src/buffer_manager.cpp

@@ -39,11 +39,30 @@ void BufferManager::new_buffer(std::string name) {
 }
 
 bool BufferManager::load_file(const std::filesystem::path& path) {
-    std::filesystem::path abs_path = std::filesystem::absolute(path);
+    std::error_code ec;
+    std::filesystem::path abs_path = std::filesystem::absolute(path, ec);
+    if (ec) {
+        spdlog::error("Failed to resolve absolute path: {}", ec.message());
+        return false;
+    }
+
+    bool path_exists = std::filesystem::exists(abs_path, ec);
 
     // Check if already open
     for (const auto& buf : buffers_) {
-        if (buf->file_path() && std::filesystem::equivalent(*buf->file_path(), abs_path)) {
+        if (!buf->file_path()) continue;
+        const auto& buf_path = *buf->file_path();
+        
+        bool is_same = false;
+        // Only use equivalent if BOTH exist on disk to avoid exceptions/errors
+        if (path_exists && std::filesystem::exists(buf_path, ec)) {
+            is_same = std::filesystem::equivalent(buf_path, abs_path, ec);
+        } else {
+            // Fallback: string comparison (handles new files or one missing)
+            is_same = (buf_path == abs_path);
+        }
+
+        if (is_same) {
             if (window_manager_.active_window()) {
                 window_manager_.active_window()->set_buffer(buf);
             }
@@ -54,16 +73,29 @@ bool BufferManager::load_file(const std::filesystem::path& path) {
         }
     }
 
-    auto new_buffer_opt = Buffer::from_file(abs_path);
-    if (!new_buffer_opt) {
-        return false;
+    // Not open, try to load or create
+    std::shared_ptr<Buffer> new_buffer_ptr;
+
+    if (path_exists) {
+        auto buf_opt = Buffer::from_file(abs_path);
+        if (buf_opt) {
+            new_buffer_ptr = std::make_shared<Buffer>(std::move(*buf_opt));
+        } else {
+            // File exists but load failed (e.g. permissions)
+            spdlog::error("Failed to load file: {}", abs_path.string());
+            return false;
+        }
+    } else {
+        // File does not exist -> Create new empty buffer for it
+        new_buffer_ptr = std::make_shared<Buffer>(abs_path.filename().string());
+        new_buffer_ptr->set_file_path(abs_path);
+        spdlog::info("New file buffer created: {}", abs_path.string());
     }
-    
-    auto new_buffer = std::make_shared<Buffer>(std::move(*new_buffer_opt));
-    buffers_.push_back(new_buffer);
+
+    buffers_.push_back(new_buffer_ptr);
 
     if (window_manager_.active_window()) {
-        window_manager_.active_window()->set_buffer(new_buffer);
+        window_manager_.active_window()->set_buffer(new_buffer_ptr);
     }
 
     notifier_.emit_event(EditorEvent::BufferModified);