# Editor Parity Plan: rab → pi 1/1
Comprehensive gap analysis between rab's TUI editor/autocomplete/input
and pi's reference implementation (`packages/tui/src/`).
---
## 🔴 CRITICAL (non-functional — user-facing bugs)
### 1. Paste Completely Broken
| Input pipeline | `StdinBuffer` detects bracketed paste `\x1b[200~…\x1b[201~`, emits `paste` event, terminal rewraps with markers and sends through `handleInput` | `poll_key_event` in `terminal.rs` discards `Event::Paste` — returns `Ok(None)` for any non-`Key`, non-`Resize` event |
| Editor entry | `handleInput` sees `\x1b[200~`, buffers, calls `handlePaste` | `Editor::handle_paste` exists but is NEVER called |
**Fix**: Handle `Event::Paste` from crossterm in the input loop, route to `Editor::handle_paste` (or wrap with bracketed markers and let Editor's existing detection handle it).
**Files**: `src/tui/terminal.rs` (`poll_key_event`, `read_key_event`), `src/agent/ui/app.rs` (event loop), `src/tui/components/editor.rs` (paste delivery path).
---
### 2. @ Autocomplete Broken
| File search | `fd` binary — fast, respects `.gitignore`, fuzzy across project | `std::fs::read_dir` only — slow, no `.gitignore`, directory-only |
| Fuzzy matching | `fuzzyFilter` + `scoreEntry` — scores by exact/starts/contains | `starts_with` only |
| Quoted prefix | `@"path with spaces"` supported | Not supported (no `@"` parser) |
| Debounce | 20ms for attachment `@` | No debounce |
| Trigger on letters in @ ctx | Checks `autocompleteTriggerPattern` regex: `(?:^\|[\s])[@#]\S*$` | Checks `text_before.contains('@') \|\| text_before.contains('#')` — simpler but misses edge cases |
**Fix**: Add `fd` detection + `walkDirectoryWithFd` equivalent, implement fuzzy file search, support `@"` quoted prefix, add debounce timer.
**Files**: `src/tui/autocomplete.rs` (`CombinedAutocompleteProvider`), `src/tui/components/editor.rs` (`update_autocomplete`).
---
### 3. / Autocomplete Behavior Differences
| Argument completion | `SlashCommand.getArgumentCompletions(prefix)` | Not supported |
| Matching | `fuzzyFilter` for names | Case-insensitive `starts_with` |
| Layout | `SLASH_COMMAND_SELECT_LIST_LAYOUT` (min 12, max 32) | Default SelectList layout |
| After Enter select | Clear autocomplete + submit | Same (correct) |
**Fix**: Add `SlashCommand::argument_completions` trait, add fuzzy matching for slash names, add special layout.
**Files**: `src/tui/autocomplete.rs` (`SlashCommand`), `src/tui/components/editor.rs` (autocomplete layout).
---
## 🟡 EDITOR COMPONENT (functional gaps — causes visual/UX bugs)
### 4. Word Wrapping Algorithm
| Function | `wordWrapLine` — paste-marker aware via `segmentWithMarkers`, CJK break support, word-boundary backtracking | `wrap_text_with_ansi` + `wrap_single_line` + `split_into_tokens` |
| Paste marker awareness | Merges paste markers into atomic segments so they never split across visual lines | No awareness — `[paste #1` can wrap mid-marker |
| Output shape | Returns `TextChunk[]` with `startIndex`/`endIndex` for cursor mapping | Returns `Vec<String>` — cursor mapping done separately by `layout_text` via `visual_col_to_byte_offset` |
**Impact**: Likely root cause of **line duplication** and **wrapping artifacts** users report. The two algorithms produce fundamentally different chunk boundaries.
**Fix**: Port `segmentWithMarkers` + `wordWrapLine` from pi to Rust, or make `wrap_text_with_ansi` paste-marker aware and return structured chunks with byte offsets.
**Files**: `src/tui/components/editor.rs` (`layout_text`), `src/tui/util.rs` (`wrap_text_with_ansi`).
---
### 5. Paste Markers Not Atomic in Cursor Movement
| Cursor left/right | `segment(text, "grapheme")` — paste-marker aware via `segmentWithMarkers`, treats `[paste #1]` as one grapheme | Direct `grapheme_indices(true)` — cursor can land in the middle of `[paste #1]` |
| Word navigation | `findWordForward`/`findWordBackward` receive `{ isAtomicSegment: isPasteMarker }` | `find_word_forward_with`/`find_word_backward_with` support `is_atomic_segment` but Editor never passes it |
**Fix**: Make `move_left`/`move_right` skip paste marker boundaries, pass `is_atomic_segment` to word navigation calls.
**Files**: `src/tui/components/editor.rs` (`move_left`, `move_right`, `delete_word_backward`, `delete_word_forward`).
---
### 6. Sticky Column Vertical Movement
| Decision table | Full P/S/T/U table with 7 scenarios | `preferred_col.unwrap_or(cursor_col).min(target_len)` |
| Snap-to-segment | `snappedFromCursorCol` + multi-visual-line resolution for paste markers | Not implemented |
| Preferred column tracking | `preferredVisualCol` set/cleared per decision-table rules | Simple state, no decision table |
**Fix**: Implement the decision table from pi (`computeVerticalMoveColumn`), add snap-to-segment for atomic segments.
**Files**: `src/tui/components/editor.rs` (`move_vertical`, `move_up`, `move_down`).
---
### 7. Max Visible Lines
| Formula | `Math.max(5, Math.floor(terminalRows * 0.3))` — dynamic per terminal height | Fixed `max_visible_lines` from `EditorOptions` (default 10) |
**Fix**: Compute dynamically from TUI terminal height.
**Files**: `src/tui/components/editor.rs` (`render`).
---
### 8. Autocomplete Pre-select Best Match
| Pre-select | `getBestAutocompleteMatchIndex`: exact match first, then prefix match | Always starts at index 0 |
**Fix**: Implement `get_best_autocomplete_match_index` equivalent.
**Files**: `src/tui/components/editor.rs` (`set_autocomplete` / `trigger_autocomplete`).
---
### 9. Cursor Overflow into Padding
| Behavior | When cursor is at end and `paddingX > 0`, cursor can overflow into right padding. Flags `cursorInPadding` and adjusts right padding. | No special handling — cursor stuck at content boundary |
**Fix**: In `render`, detect cursor-at-end overflow and allow it into padding area.
**Files**: `src/tui/components/editor.rs` (`render`).
---
### 10. CSI-u Decoding in Paste Path
| Paste handling | Decodes `\x1b[106;5u` → Ctrl+J before filtering. Handles tmux/extended-key terminals that re-encode control bytes as CSI-u. | No CSI-u decoding — control bytes may be lost or leaked |
**Fix**: Add `decode_csi_u` function and apply it in `handle_paste` before filtering.
**Files**: `src/tui/components/editor.rs` (`handle_paste`).
---
### 11. Space Auto-insert Before Pasting Paths
| Paste logic | If pasting `/~/.` and char before cursor is a word char, prepends ` ` for readability | No such logic |
**Fix**: Match pi's heuristic.
**Files**: `src/tui/components/editor.rs` (`handle_paste`).
---
### 12. Autocomplete Re-trigger After Backspace Dismissal
| Backspace | After `handleBackspace`, if autocomplete was just dismissed, re-triggers if still in completable context | Only re-triggers if `autocomplete_active` is still true |
**Fix**: In `backspace`, check context and call `try_trigger_autocomplete` even when autocomplete was already dismissed.
**Files**: `src/tui/components/editor.rs` (`backspace`).
---
### 13. Autocomplete Debounce
| Timing | 20ms for attachment `@` patterns (when `debouncePattern` matches), 0ms for slash commands | No debounce |
**Fix**: Add debounce timer (`setTimeout` equivalent).
**Files**: `src/tui/components/editor.rs` (`try_trigger_autocomplete`, `trigger_autocomplete`).
---
### 14. Autocomplete Dropdown: Slash Command Layout
| Layout | `SLASH_COMMAND_SELECT_LIST_LAYOUT` (minPrimaryColumnWidth: 12, maxPrimaryColumnWidth: 32) | Default SelectList layout |
**Fix**: Pass special layout when prefix starts with `/`.
**Files**: `src/tui/components/editor.rs` (`autocomplete_list` creation).
---
### 15. `yankPop` Undo Safety
| yankPop | Pushes current state to undo stack, deletes previous yank, rotates ring, inserts new text | Pops undo stack (may undo more than expected if user typed between yank and yank-pop) |
**Fix**: Push undo before deleting yanked text, don't pop.
**Files**: `src/tui/components/editor.rs` (`yank_pop`).
---
### 16. Word Navigation Without Atomic Segments
| wordLeft/wordRight | Receives `{ segment: ..., isAtomicSegment: isPasteMarker }` in word nav options | `move_word_backward`/`move_word_forward` in Editor not passing `is_atomic_segment` to word nav |
**Fix**: Pass `is_atomic_segment` callback when calling word navigation.
**Files**: `src/tui/components/editor.rs` (`move_word_backward`, `move_word_forward`), `src/tui/word_nav.rs`.
---
## 🟠 INPUT COMPONENT
### 17. Bracketed Paste Unused in Input
| Input | `Input.handleInput` detects `\x1b[200~`, buffers, calls `handlePaste` (strips newlines — single-line) | `paste_buffer` and `is_in_paste` fields exist with `#[allow(dead_code)]`, never wired |
**Fix**: Wire paste events to `Input::handle_paste` (same delivery as Editor).
**Files**: `src/tui/components/input.rs`.
---
### 18. Kill Ring Accumulation
| Delete word/line | Passes `accumulate: this.lastAction === "kill"` so consecutive kills chain in ring | All kill operations pass `false` for accumulate (no chaining) |
**Fix**: Track `last_action == "kill"` and pass to `kill_ring.push`.
**Files**: `src/tui/components/input.rs`, `src/tui/components/editor.rs`.
---
### 19. `yankPop` in Input
| yankPop | Rotates ring after deleting previous yanked text | Rotates then pops undo — different semantics |
**Fix**: Match pi's yankPop: push undo, delete, rotate, insert.
**Files**: `src/tui/components/input.rs`.
---
## 🟣 AUTOCOMPLETE PROVIDER
### 20. No `fd` Binary Integration
| File search | Spawns `fd` with `--base-directory`, `--max-results`, `--type f/d`, `--follow`, `--hidden`, `--exclude .git`. Supports `--full-path` for scoped queries. Parses output. | `std::fs::read_dir` with basic filtering |
| Performance | Fast, respects .gitignore, finds files anywhere in project | Slow in large dirs, no .gitignore |
| Scoping | `resolveScopedFuzzyQuery` resolves `src/foo/` to base+query | Basic path splitting |
**Fix**: Implement `walk_directory_with_fd` equivalent using `std::process::Command`.
**Files**: `src/tui/autocomplete.rs` (`CombinedAutocompleteProvider`).
---
### 21. No Quote-Aware Prefix
| `@"path"` | `extractQuotedPrefix` detects unclosed `"` or `@"`, returns full quoted prefix. `parsePathPrefix` splits into `isAtPrefix`, `isQuotedPrefix`, `rawPrefix`. `buildCompletionValue` closes quotes. | Not handled |
**Fix**: Add quote-parsing functions to `CombinedAutocompleteProvider`.
**Files**: `src/tui/autocomplete.rs`.
---
### 22. No Argument Completion
| SlashCommand | Has `getArgumentCompletions?(argumentPrefix)` method | No argument completion concept |
| Example | `/model <TAB>` shows model names | Not possible |
**Fix**: Add `argument_completions` trait/field to `SlashCommand`.
**Files**: `src/tui/autocomplete.rs`.
---
## 🔵 AGENT-LEVEL CHAT EDITOR
### 23. Enter/Submit Flow (Escape Priority)
| Escape handling | `CustomEditor.handleInput`: checks `app.interrupt` → if autocomplete active, delegates to `super.handleInput` (Editor cancels autocomplete). If not, fires `onEscape` or `actionHandlers.get("app.interrupt")` | `ChatEditor.handle_input`: checks `select.cancel` first → if autocomplete active, delegates to `editor.handle_input`. Then checks `app.escape`. Same result but different action constants |
**Status**: Functionally equivalent. No action needed unless action names diverge.
---
### 24. Paste Image Handler
| Handler | `onPasteImage` callback, triggered by `app.clipboard.pasteImage` keybinding | Not implemented |
**Fix**: Add paste image support if needed.
**Files**: `src/agent/ui/chat_editor.rs`.
---
## ⚪ MINOR / COSMETIC
| 25 | `setText` cursor placement | Has `cursorPlacement` param (`start`/`end`) | Always end | P4 |
| 26 | Dynamic editor height | 30% of terminal | Fixed 10 lines | P3 |
| 27 | Mouse support | `wantsKeyRelease`, mouse events | None | P5 |
| 28 | History dedup on add | Skips consecutive duplicates | `add_to_history` pushes unconditionally | P3 |
---
## Implementation Order
```
P0 ─────────────────────────────────────────────────────
1. Paste delivery (terminal.rs + app.rs event loop)
2. @ autocomplete: fd+provider fix (autocomplete.rs + editor.rs)
P1 ─────────────────────────────────────────────────────
3. Word wrapping: port wordWrapLine (editor.rs + util.rs)
4. Atomic paste markers in cursor move (editor.rs)
5. Sticky column: decision table (editor.rs)
P2 ─────────────────────────────────────────────────────
6. Autocomplete debounce (editor.rs)
7. Autocomplete argument completion (autocomplete.rs)
8. Autocomplete best-match pre-select (editor.rs)
9. Slash command layout (editor.rs)
10. CSI-u paste decoding (editor.rs)
11. Space auto-insert before path paste (editor.rs)
12. Cursor overflow into padding (editor.rs)
13. yankPop undo safety (editor.rs + input.rs)
P3 ─────────────────────────────────────────────────────
14. Dynamic editor height (editor.rs)
15. Kill ring accumulation (editor.rs + input.rs)
16. Autocomplete re-trigger after backspace (editor.rs)
17. Quoted @ prefix (autocomplete.rs)
18. History dedup (editor.rs)
19. Word nav atomic segments (editor.rs)
P4 ─────────────────────────────────────────────────────
20. fd integration (autocomplete.rs)
21. setText cursor placement param (editor.rs)
22. Input bracketed paste wiring (input.rs)
P5 ─────────────────────────────────────────────────────
23. Mouse support (tui_core.rs + editor.rs)
24. Paste image (chat_editor.rs)
```