Commit 11742b3
authored
feat(tui): implement TUI input view milestone 1 (warpdotdev#13064)
## Description
Implements the TUI input view (Milestone 1) as specified in
`specs/tui-input-view/TECH.md`. This is the first functional
editor-backed text input for the TUI rendering path.
**What's in this PR:**
### Prerequisite refactor in `crates/editor/` (~580 LOC + ~200 LOC
tests, no GUI-path regressions)
- **`ColumnUnit` enum** on `SoftWrapPoint`: replaces the bare `Pixels`
column with an explicit `ColumnUnit::Pixels(Pixels)` /
`ColumnUnit::Chars(u16)` discriminant so the GUI and TUI coordinate
spaces are enforced at the type level (cross-variant ops
`debug_assert!`). The ~15 construction sites and
`SelectionModel::goal_xs` (now `Option<Vec1<ColumnUnit>>`) /
`NavigationResult::goal_x` are updated; `navigate_line` sticky-column
logic is unchanged in shape.
- **`LayoutMode` on `RenderState`**: adds
`LayoutMode::CharCell(CharCellState)` alongside the existing
`LayoutMode::Pixels` path (`CharCellState` holds `terminal_width`,
`line_starts`, `total_chars`). In CharCell mode `handle_layout_action`
skips font shaping entirely, and `offset_to_softwrap_point` /
`softwrap_point_to_offset` / `max_line` use pure char-cell integer
arithmetic. New APIs: `RenderState::new_tui(terminal_width, styles,
ctx)`, `is_char_cell_mode()`, `update_char_cell_text(text)`,
`set_char_cell_terminal_width(width)`. The styles field is retained for
API compatibility but unused in CharCell mode.
- **`usize → u32` overflow guard**: `char_cell_softwrap_point_to_offset`
treats the final (unbounded) logical line as spanning all remaining
rows, so a target row past the end resolves there instead of
overflowing. Covered by the round-trip unit tests.
### Editor-backed TUI input (no separate model)
There is **no** `TuiInputModel` — the input reuses the existing
`CodeEditorModel` in char-cell mode:
- `app/src/code/editor/model.rs`: adds
`CodeEditorModel::new_tui(terminal_width, ctx)` (shares sub-model wiring
with the GUI `new()` via a `from_content(..)` helper) and
`set_tui_terminal_width(..)`. Its
`CoreEditorModel::on_buffer_version_updated` override drives
`update_char_cell_text` synchronously on every edit when in char-cell
mode (the async font-shaping pipeline is bypassed there). Re-exported
for the TUI front-end via `app/src/editor/mod.rs`.
- `crates/warp_tui/src/input/` (~1.2k LOC incl. tests):
- `kill_buffer.rs`: single-entry kill buffer (Ctrl+K / Ctrl+U / Ctrl+W →
Ctrl+Y yank)
- `view.rs`: `TuiInputView` implementing `TuiView` + `TypedActionView`.
Holds `ModelHandle<CodeEditorModel>` plus TUI session state (kill
buffer, scroll offset, terminal width, `max_visible_rows = 6`). Full
Emacs/readline keybinding dispatch via a `TuiInputAction` enum
(Ctrl+A/E/B/F/P/N/K/U/W/Y/H/D/Z, word movement, Shift+arrows,
Shift+Enter). Renders via pure char-cell helpers; `TuiInputElement`
paints rows, applies `Modifier::REVERSED` to selection spans, and
reports the block cursor via `cursor_position()`. Emits
`TuiInputViewEvent::Submitted(String)` on Enter.
- `view_tests.rs`: drives a real `CodeEditorModel` + `TuiInputView`.
### Spec + examples
- `specs/tui-input-view/TECH.md`: as-built architecture spec (design
rationale, keybinding table, follow-up scope for runtime wiring, input
mode, slash commands, history).
- `crates/warp_tui/examples/tui_input_demo.rs`: interactive
editor-backed input demo (`cargo run -p warp_tui --example
tui_input_demo`).
- `crates/warpui_core/examples/tui_file_viewer.rs`: validates the TUI
rendering/runtime pipeline independently of the editor (scrollable file
viewer).
## Linked Issue
N/A — spec-driven feature, see `specs/tui-input-view/TECH.md`
## Testing
- `crates/editor/src/render/model/mod_tests.rs::char_cell`: 12 unit
tests (max_line, `offset → point → offset` round-trips at multiple
widths, `ColumnUnit::Chars` correctness, multi-line/wrapping
boundaries).
- `crates/warp_tui/src/input/view_tests.rs`: 12 tests driving a real
`CodeEditorModel` + `TuiInputView` (cursor placement, blank-line
navigation, selection text, kill/yank).
- Existing `warp_editor` test suite continues to pass (no GUI-path
behaviour changes).
- `./script/format` and `cargo clippy --workspace --all-targets --tests
-- -D warnings` pass.
Run the examples from a real terminal:
```sh
cargo run -p warp_tui --example tui_input_demo
cargo run -p warpui_core --example tui_file_viewer --features tui -- specs/tui-input-view/TECH.md
```
Wiring `TuiInputView` into the `warp-tui` binary's runtime (today only
auth runs there) is the next step.
## Agent Mode
- [x] Warp Agent Mode - This PR was created via Warp's AI Agent Mode
Conversation:
https://staging.warp.dev/conversation/c5b70ee0-60c6-49f9-908c-2e3275949d37
CHANGELOG-NEW-FEATURE: TUI input view (Milestone 1): multi-line,
Emacs/readline keybindings, editor-backed via CodeEditorModel +
SelectionModel + CharCell RenderState1 parent ef0ac45 commit 11742b3
35 files changed
Lines changed: 3337 additions & 233 deletions
File tree
- app
- src
- code/editor
- editor
- tui
- crates
- editor
- src
- render/model
- warp_tui
- examples
- src
- input
- warpui_core
- examples
- src
- core
- view
- elements/tui
- presenter
- runtime
- specs/tui-input-view
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
463 | 463 | | |
464 | 464 | | |
465 | 465 | | |
466 | | - | |
| 466 | + | |
467 | 467 | | |
468 | 468 | | |
469 | 469 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
47 | 47 | | |
48 | 48 | | |
49 | 49 | | |
50 | | - | |
51 | | - | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
52 | 53 | | |
53 | 54 | | |
54 | 55 | | |
| |||
332 | 333 | | |
333 | 334 | | |
334 | 335 | | |
| 336 | + | |
| 337 | + | |
| 338 | + | |
| 339 | + | |
| 340 | + | |
| 341 | + | |
| 342 | + | |
| 343 | + | |
| 344 | + | |
| 345 | + | |
| 346 | + | |
| 347 | + | |
| 348 | + | |
| 349 | + | |
| 350 | + | |
| 351 | + | |
| 352 | + | |
| 353 | + | |
| 354 | + | |
| 355 | + | |
| 356 | + | |
| 357 | + | |
| 358 | + | |
| 359 | + | |
| 360 | + | |
| 361 | + | |
| 362 | + | |
| 363 | + | |
| 364 | + | |
| 365 | + | |
| 366 | + | |
| 367 | + | |
| 368 | + | |
| 369 | + | |
| 370 | + | |
| 371 | + | |
| 372 | + | |
| 373 | + | |
| 374 | + | |
| 375 | + | |
| 376 | + | |
| 377 | + | |
| 378 | + | |
| 379 | + | |
| 380 | + | |
| 381 | + | |
| 382 | + | |
| 383 | + | |
| 384 | + | |
| 385 | + | |
| 386 | + | |
| 387 | + | |
| 388 | + | |
| 389 | + | |
| 390 | + | |
| 391 | + | |
| 392 | + | |
| 393 | + | |
| 394 | + | |
| 395 | + | |
| 396 | + | |
| 397 | + | |
| 398 | + | |
| 399 | + | |
| 400 | + | |
| 401 | + | |
335 | 402 | | |
336 | 403 | | |
337 | 404 | | |
| |||
355 | 422 | | |
356 | 423 | | |
357 | 424 | | |
358 | | - | |
359 | | - | |
360 | | - | |
361 | | - | |
| 425 | + | |
362 | 426 | | |
363 | 427 | | |
364 | 428 | | |
| |||
388 | 452 | | |
389 | 453 | | |
390 | 454 | | |
391 | | - | |
| 455 | + | |
392 | 456 | | |
393 | 457 | | |
394 | 458 | | |
395 | 459 | | |
396 | | - | |
397 | | - | |
| 460 | + | |
| 461 | + | |
398 | 462 | | |
399 | 463 | | |
400 | 464 | | |
401 | 465 | | |
| 466 | + | |
| 467 | + | |
| 468 | + | |
| 469 | + | |
| 470 | + | |
| 471 | + | |
| 472 | + | |
| 473 | + | |
| 474 | + | |
| 475 | + | |
| 476 | + | |
| 477 | + | |
| 478 | + | |
| 479 | + | |
| 480 | + | |
| 481 | + | |
| 482 | + | |
| 483 | + | |
| 484 | + | |
| 485 | + | |
| 486 | + | |
| 487 | + | |
| 488 | + | |
| 489 | + | |
| 490 | + | |
| 491 | + | |
| 492 | + | |
| 493 | + | |
| 494 | + | |
| 495 | + | |
| 496 | + | |
| 497 | + | |
| 498 | + | |
| 499 | + | |
| 500 | + | |
| 501 | + | |
| 502 | + | |
| 503 | + | |
| 504 | + | |
| 505 | + | |
| 506 | + | |
| 507 | + | |
| 508 | + | |
| 509 | + | |
| 510 | + | |
| 511 | + | |
| 512 | + | |
| 513 | + | |
| 514 | + | |
| 515 | + | |
| 516 | + | |
| 517 | + | |
| 518 | + | |
| 519 | + | |
| 520 | + | |
| 521 | + | |
| 522 | + | |
| 523 | + | |
| 524 | + | |
| 525 | + | |
| 526 | + | |
| 527 | + | |
| 528 | + | |
| 529 | + | |
| 530 | + | |
| 531 | + | |
| 532 | + | |
| 533 | + | |
| 534 | + | |
| 535 | + | |
| 536 | + | |
| 537 | + | |
| 538 | + | |
| 539 | + | |
| 540 | + | |
| 541 | + | |
| 542 | + | |
| 543 | + | |
| 544 | + | |
402 | 545 | | |
403 | 546 | | |
404 | 547 | | |
| |||
1588 | 1731 | | |
1589 | 1732 | | |
1590 | 1733 | | |
1591 | | - | |
1592 | | - | |
1593 | 1734 | | |
1594 | 1735 | | |
1595 | 1736 | | |
| |||
1598 | 1739 | | |
1599 | 1740 | | |
1600 | 1741 | | |
| 1742 | + | |
1601 | 1743 | | |
1602 | 1744 | | |
1603 | 1745 | | |
| |||
2435 | 2577 | | |
2436 | 2578 | | |
2437 | 2579 | | |
2438 | | - | |
| 2580 | + | |
2439 | 2581 | | |
2440 | 2582 | | |
2441 | 2583 | | |
| |||
2476 | 2618 | | |
2477 | 2619 | | |
2478 | 2620 | | |
2479 | | - | |
| 2621 | + | |
| 2622 | + | |
2480 | 2623 | | |
2481 | 2624 | | |
2482 | | - | |
| 2625 | + | |
2483 | 2626 | | |
2484 | 2627 | | |
2485 | 2628 | | |
| |||
3649 | 3792 | | |
3650 | 3793 | | |
3651 | 3794 | | |
3652 | | - | |
3653 | | - | |
| 3795 | + | |
| 3796 | + | |
3654 | 3797 | | |
3655 | 3798 | | |
3656 | 3799 | | |
| 3800 | + | |
| 3801 | + | |
| 3802 | + | |
| 3803 | + | |
| 3804 | + | |
| 3805 | + | |
| 3806 | + | |
| 3807 | + | |
3657 | 3808 | | |
3658 | 3809 | | |
3659 | 3810 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
13 | 13 | | |
14 | 14 | | |
15 | 15 | | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
16 | 20 | | |
17 | 21 | | |
18 | 22 | | |
| |||
File renamed without changes.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
46 | 46 | | |
47 | 47 | | |
48 | 48 | | |
| 49 | + | |
49 | 50 | | |
50 | 51 | | |
51 | 52 | | |
| |||
0 commit comments