# Lumacs Message System Improvement Plan

## Current State Analysis

### What Works
1. **`*Messages*` buffer exists** - Messages are logged to it via `EditorCore::set_message()`
2. **Transient echo area** - Single-line message display at bottom with 3-second auto-clear
3. **Basic Lua API** - `editor:message(text)` works for simple messages

### Pain Points
1. **Long messages are truncated** - No visual indication that text was cut off
2. **`*Messages*` buffer not accessible** - Created with `create_buffer_no_window()`, not in buffer list
3. **No message history navigation** - Users can't review past messages
4. **No message severity levels** - All messages treated equally
5. **No overflow indicator** - Users don't know if they're seeing the full message

---

## Proposed Solution: Emacs-Style Message System

### Architecture Overview

```
┌─────────────────────────────────────────────────────────────┐
│                       User Input                             │
│  editor:message("text")  /  editor:message("text", "error") │
└─────────────────────────┬───────────────────────────────────┘
                          ▼
┌─────────────────────────────────────────────────────────────┐
│                  EditorCore::set_message()                   │
│  1. Log to *Messages* buffer with timestamp                  │
│  2. Determine display behavior based on length/severity      │
│  3. Store in transient message slot                          │
│  4. Emit EditorEvent::Message                                │
└─────────────────────────┬───────────────────────────────────┘
                          ▼
┌─────────────────────────────────────────────────────────────┐
│                    Echo Area Rendering                       │
│  IF message fits on one line:                                │
│    → Display full message                                    │
│  ELSE IF message is multi-line OR too long:                 │
│    → Display first line + "[...see *Messages*]"             │
│    OR                                                        │
│    → Display truncated + "..." indicator                     │
└─────────────────────────────────────────────────────────────┘
```

---

## Implementation Plan

### Phase 1: Make *Messages* Buffer Accessible (Priority: HIGH)

**Goal:** Users can view message history via `C-x b *Messages*` or `M-x view-messages`

#### 1.1 Change buffer creation method
```cpp
// Current (hidden):
messages_buf = buffer_manager_->create_buffer_no_window("*Messages*");

// New (accessible):
messages_buf = buffer_manager_->create_buffer_no_window("*Messages*");
// Buffer is already in buffer list, just needs to be queryable
```

**Files to modify:**
- `src/buffer_manager.cpp` - Ensure `*Messages*` appears in `get_buffer_names()`
- `src/editor_core.cpp` - Add timestamp to logged messages

#### 1.2 Add `view-messages` command
```lua
-- In defaults.hpp
editor:register_command("view-messages", "View the *Messages* buffer", function()
    local buf = editor:get_buffer_by_name("*Messages*")
    if buf then
        editor:switch_buffer_in_window("*Messages*")
        editor:goto_end()
    else
        editor:message("No messages yet")
    end
end, false)

-- Keybinding (optional, Emacs uses C-h e or M-x view-echo-area-messages)
editor:bind_key("C-h e", "view-messages")
```

#### 1.3 Add timestamps to messages
```cpp
void EditorCore::set_message(std::string msg) {
    if (buffer_manager_) {
        auto messages_buf = buffer_manager_->get_buffer_by_name("*Messages*");
        if (!messages_buf) {
            messages_buf = buffer_manager_->create_buffer_no_window("*Messages*");
        }
        if (messages_buf) {
            // Add timestamp
            auto now = std::chrono::system_clock::now();
            auto time_t = std::chrono::system_clock::to_time_t(now);
            std::stringstream ss;
            ss << "[" << std::put_time(std::localtime(&time_t), "%H:%M:%S") << "] " << msg << "\n";

            // Append to buffer
            size_t last_line = messages_buf->line_count() > 0 ? messages_buf->line_count() - 1 : 0;
            size_t last_col = messages_buf->line_count() > 0 ? messages_buf->lines().back().length() : 0;
            messages_buf->insert({last_line, last_col}, ss.str());
        }
    }
    // ... rest of existing code
}
```

---

### Phase 2: Add Truncation Indicator (Priority: HIGH)

**Goal:** Users know when messages are truncated

#### 2.1 GTK Renderer - Add ellipsis for long messages
```cpp
// In gtk_renderer.cpp render_minibuffer()
void GtkRenderer::render_minibuffer(/* ... */) {
    // ... existing code ...

    if (!core_.minibuffer_manager().is_active() && !core_.last_message().empty()) {
        std::string msg = core_.last_message();

        // Calculate available width
        double available_width = width - PADDING_LEFT - PADDING_RIGHT;

        // Create layout to measure text
        auto layout = Pango::Layout::create(context_widget_.get_pango_context());
        layout->set_font_description(font_desc_);
        layout->set_text(msg);

        int text_width, text_height;
        layout->get_pixel_size(text_width, text_height);

        // If text is too wide, truncate with indicator
        if (text_width > available_width) {
            const std::string suffix = "... [C-h e for full message]";
            // Binary search for max chars that fit
            size_t max_chars = find_max_fitting_chars(layout, msg, available_width, suffix);
            msg = msg.substr(0, max_chars) + suffix;
            layout->set_text(msg);
        }

        // Render
        cr->move_to(minibuffer_x, minibuffer_y);
        layout->show_in_cairo_context(cr);
    }
}
```

#### 2.2 TUI Renderer - Add ellipsis for long messages
```cpp
// In tui_editor.cpp render()
void TuiEditor::render_minibuffer_area() {
    // ... existing code ...

    if (!core_->minibuffer_manager().is_active() && !message_line_.empty()) {
        std::string msg = message_line_;

        // Calculate available width (leave room for indicator)
        int available_width = width_ - 2;

        if (msg.length() > static_cast<size_t>(available_width)) {
            const std::string suffix = "...[C-h e]";
            size_t max_chars = available_width - suffix.length();
            msg = msg.substr(0, max_chars) + suffix;
        }

        mvprintw(height_ - 1, 0, "%s", msg.c_str());
    }
}
```

---

### Phase 3: Message Severity Levels (Priority: MEDIUM)

**Goal:** Different message types are visually distinct and have different behaviors

#### 3.1 Add MessageSeverity enum
```cpp
// In editor_core.hpp
enum class MessageSeverity {
    Info,       // Normal messages, 3-second timeout
    Warning,    // Yellow/orange, 5-second timeout
    Error,      // Red, no auto-clear until user input
    Debug       // Only logged to *Messages*, not displayed in echo area
};

// Enhanced set_message signature
void set_message(std::string msg, MessageSeverity severity = MessageSeverity::Info);
```

#### 3.2 Update Lua API
```lua
-- Enhanced API
editor:message("File saved")                    -- Info (default)
editor:message("Buffer modified", "warning")   -- Warning
editor:message("File not found!", "error")     -- Error
editor:log("Debug info")                        -- Debug (log only)
```

#### 3.3 Severity-based rendering
```cpp
// Different colors based on severity
Color get_message_color(MessageSeverity severity) {
    switch (severity) {
        case MessageSeverity::Info:    return fg_color;
        case MessageSeverity::Warning: return Color(255, 200, 0);  // Yellow/Orange
        case MessageSeverity::Error:   return Color(255, 80, 80);  // Red
        default:                       return fg_color;
    }
}

// Different timeout based on severity
std::chrono::seconds get_message_timeout(MessageSeverity severity) {
    switch (severity) {
        case MessageSeverity::Info:    return std::chrono::seconds(3);
        case MessageSeverity::Warning: return std::chrono::seconds(5);
        case MessageSeverity::Error:   return std::chrono::seconds(0);  // No auto-clear
        default:                       return std::chrono::seconds(3);
    }
}
```

---

### Phase 4: Multi-line Message Support (Priority: LOW)

**Goal:** Important messages can span multiple lines in the echo area

#### 4.1 Expandable echo area
For very important messages (errors, confirmations), allow the echo area to expand:

```cpp
// Track if echo area should expand
bool echo_area_expanded_ = false;
size_t echo_area_lines_ = 1;  // 1 = normal, 2-3 for expanded

// Auto-expand for multi-line messages
void set_message(std::string msg, MessageSeverity severity) {
    size_t newlines = std::count(msg.begin(), msg.end(), '\n');
    if (newlines > 0 && severity >= MessageSeverity::Warning) {
        echo_area_expanded_ = true;
        echo_area_lines_ = std::min(newlines + 1, 3UL);  // Max 3 lines
    }
    // ... rest of implementation
}
```

This is lower priority because it requires significant rendering changes.

---

### Phase 5: Message History Navigation (Priority: LOW)

**Goal:** Users can cycle through recent messages without switching to `*Messages*` buffer

```lua
-- M-p to show previous message
-- M-n to show next message (when in message history mode)

local message_history_index = nil

editor:bind_key("M-p", function()
    -- Show previous message from history
    -- This would require C++ support to track message history
end)
```

This is lower priority because `view-messages` command provides this functionality.

---

## Implementation Order

| Phase | Description | Effort | Impact | Priority |
|-------|-------------|--------|--------|----------|
| 1.1   | Make *Messages* in buffer list | Low | High | **P0** |
| 1.2   | Add view-messages command | Low | High | **P0** |
| 1.3   | Add timestamps to messages | Low | Medium | **P0** |
| 2.1   | GTK truncation indicator | Medium | High | **P1** |
| 2.2   | TUI truncation indicator | Medium | High | **P1** |
| 3.x   | Message severity levels | Medium | Medium | **P2** |
| 4.x   | Multi-line echo area | High | Low | **P3** |
| 5.x   | Message history navigation | Medium | Low | **P3** |

---

## Quick Wins (Can implement immediately)

### 1. Add `view-messages` command to defaults.hpp

```lua
-- Add to COMMANDS section of defaults.hpp
editor:register_command("view-messages", "View the *Messages* buffer", function()
    local buf = editor:get_buffer_by_name("*Messages*")
    if buf then
        editor:switch_buffer_in_window("*Messages*")
        editor:goto_end()
        editor:message("Viewing *Messages* buffer")
    else
        editor:message("No messages logged yet")
    end
end, false)

-- Add keybinding
editor:bind_key("C-h e", "view-messages")
```

### 2. Add timestamp to messages in C++

Small change to `EditorCore::set_message()` to prefix messages with `[HH:MM:SS]`.

### 3. Verify *Messages* buffer appears in buffer list

Check if `create_buffer_no_window` adds buffer to the list returned by `get_buffer_names()`.

---

## Testing Checklist

- [ ] `M-x view-messages` opens *Messages* buffer
- [ ] `C-h e` opens *Messages* buffer
- [ ] `C-x b *Messages*` switches to *Messages* buffer
- [ ] Long messages show truncation indicator
- [ ] Messages have timestamps in *Messages* buffer
- [ ] Different severity messages have different colors (if implemented)
- [ ] Error messages don't auto-clear (if implemented)
- [ ] All 32 existing tests still pass

---

## Comparison with Emacs

| Feature | Emacs | Lumacs Current | Lumacs Planned |
|---------|-------|----------------|----------------|
| Echo area | ✓ | ✓ | ✓ |
| *Messages* buffer | ✓ | ✓ (hidden) | ✓ (accessible) |
| `view-echo-area-messages` | ✓ | ✗ | ✓ |
| Truncation indicator | ✓ | ✗ | ✓ |
| Message severity | Partial | ✗ | Planned |
| Multi-line echo | ✓ | ✗ | Optional |
| Message history nav | ✗ | ✗ | Optional |

This plan brings Lumacs closer to Emacs behavior while keeping the implementation manageable.
