瀏覽代碼

refactor(frontend): Phase 3 - extract shared MessageFormatter utility

Create a shared MessageFormatter utility class for consistent message
formatting across both GTK and TUI frontends:

- collapse_multiline(): Show first line with count indicator
- truncate_to_width(): Truncate with help hint suffix
- format_for_echo_area(): Combined formatting for display

Eliminates ~30 lines of duplicated message formatting code.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Bernardo Magri 1 月之前
父節點
當前提交
fb20590fbd
共有 3 個文件被更改,包括 76 次插入37 次删除
  1. 69 0
      include/lumacs/message_formatter.hpp
  2. 3 13
      src/gtk_renderer.cpp
  3. 4 24
      src/tui_editor.cpp

+ 69 - 0
include/lumacs/message_formatter.hpp

@@ -0,0 +1,69 @@
+#pragma once
+
+#include <string>
+#include <cstddef>
+
+namespace lumacs {
+
+/// @brief Shared message formatting utilities for both GTK and TUI frontends.
+///
+/// This utility provides consistent message formatting including:
+/// - Multi-line message collapsing (show first line + count)
+/// - Message truncation with help hint
+class MessageFormatter {
+public:
+    /// Truncation suffix shown when message is too long
+    static constexpr const char* TRUNCATION_SUFFIX = "... [C-h e]";
+
+    /// @brief Collapse a multi-line message to show only the first line with a count indicator.
+    /// @param message The original message (may contain newlines)
+    /// @return The collapsed message with line count indicator if multi-line, otherwise unchanged
+    static std::string collapse_multiline(const std::string& message) {
+        size_t newline_pos = message.find('\n');
+        if (newline_pos == std::string::npos) {
+            return message;  // Single line, return as-is
+        }
+
+        // Count additional lines
+        size_t line_count = 1;
+        size_t pos = 0;
+        while ((pos = message.find('\n', pos)) != std::string::npos) {
+            ++line_count;
+            ++pos;
+        }
+
+        // Return first line with indicator
+        return message.substr(0, newline_pos) +
+               " [+" + std::to_string(line_count - 1) + " lines, C-h e]";
+    }
+
+    /// @brief Truncate a message to fit within a maximum character width.
+    /// @param message The message to truncate
+    /// @param max_width Maximum number of characters allowed
+    /// @return The truncated message with suffix if it was too long, otherwise unchanged
+    static std::string truncate_to_width(const std::string& message, size_t max_width) {
+        if (message.length() <= max_width) {
+            return message;
+        }
+
+        const std::string suffix = TRUNCATION_SUFFIX;
+        if (max_width <= suffix.length()) {
+            return suffix.substr(0, max_width);  // Edge case: very narrow width
+        }
+
+        size_t max_chars = max_width - suffix.length();
+        return message.substr(0, max_chars) + suffix;
+    }
+
+    /// @brief Format a message for display in the echo area.
+    /// Combines multi-line collapsing and width truncation.
+    /// @param message The original message
+    /// @param max_width Maximum display width in characters
+    /// @return The formatted message ready for display
+    static std::string format_for_echo_area(const std::string& message, size_t max_width) {
+        std::string result = collapse_multiline(message);
+        return truncate_to_width(result, max_width);
+    }
+};
+
+} // namespace lumacs

+ 3 - 13
src/gtk_renderer.cpp

@@ -2,6 +2,7 @@
 #include "lumacs/editor_core.hpp"
 #include "lumacs/minibuffer_manager.hpp" // For MinibufferManager and MinibufferMode
 #include "lumacs/keybinding.hpp" // For KeyBindingManager
+#include "lumacs/message_formatter.hpp"
 #include <algorithm> // For std::max, std::min
 #include <spdlog/spdlog.h> // Added for debug logging
 
@@ -514,19 +515,8 @@ void GtkRenderer::render_minibuffer(const Cairo::RefPtr<Cairo::Context>& cr, int
                     break;
             }
 
-            // Handle multi-line messages: show first line + indicator
-            size_t newline_pos = minibuffer_text.find('\n');
-            if (newline_pos != std::string::npos) {
-                // Count additional lines
-                size_t line_count = 1;
-                size_t pos = 0;
-                while ((pos = minibuffer_text.find('\n', pos)) != std::string::npos) {
-                    ++line_count;
-                    ++pos;
-                }
-                minibuffer_text = minibuffer_text.substr(0, newline_pos) +
-                    " [+" + std::to_string(line_count - 1) + " lines, C-h e]";
-            }
+            // Handle multi-line messages: collapse to first line with indicator
+            minibuffer_text = MessageFormatter::collapse_multiline(minibuffer_text);
         } else {
             cr->set_source_rgb(fg.r / 255.0, fg.g / 255.0, fg.b / 255.0);
         }

+ 4 - 24
src/tui_editor.cpp

@@ -4,6 +4,7 @@
 #include "lumacs/mode_activator.hpp"
 #include "lumacs/keybinding.hpp"
 #include "lumacs/cursor_blink.hpp"
+#include "lumacs/message_formatter.hpp"
 #include <ncurses.h>
 #include <memory>
 #include <chrono>
@@ -659,31 +660,10 @@ void TuiEditor::render_message_line() {
         std::string key_seq = core_->keybinding_manager().current_sequence_display() + "-";
         mvprintw(msg_y, 0, "%s", key_seq.c_str());
     } else if (!message_line_.empty()) {
-        // Display transient message with truncation if needed
-        std::string display_msg = message_line_;
-
-        // Handle multi-line messages: show first line + indicator
-        size_t newline_pos = display_msg.find('\n');
-        if (newline_pos != std::string::npos) {
-            // Count lines
-            size_t line_count = 1;
-            size_t pos = 0;
-            while ((pos = display_msg.find('\n', pos)) != std::string::npos) {
-                ++line_count;
-                ++pos;
-            }
-            // Show first line with line count indicator
-            display_msg = display_msg.substr(0, newline_pos) +
-                " [+" + std::to_string(line_count - 1) + " lines, C-h e]";
-        }
-
-        const std::string suffix = "...[C-h e]";
+        // Display transient message with formatting (multi-line collapse + truncation)
         int available_width = width_ - 1;
-
-        if (static_cast<int>(display_msg.length()) > available_width) {
-            size_t max_chars = available_width - suffix.length();
-            display_msg = display_msg.substr(0, max_chars) + suffix;
-        }
+        std::string display_msg = MessageFormatter::format_for_echo_area(
+            message_line_, static_cast<size_t>(available_width));
 
         // Apply color based on severity
         int msg_attrs = attrs;