Skip to content

feat/multiline composer arrow navigation#1719

Closed
aboimpinto wants to merge 3 commits into
Hmbown:mainfrom
aboimpinto:feat/multiline-composer-arrow-navigation
Closed

feat/multiline composer arrow navigation#1719
aboimpinto wants to merge 3 commits into
Hmbown:mainfrom
aboimpinto:feat/multiline-composer-arrow-navigation

Conversation

@aboimpinto

@aboimpinto aboimpinto commented May 16, 2026

Copy link
Copy Markdown
Contributor

See #1720
See #1721

Problem

Issue 1 (#1720): On Windows, pressing Up on an empty composer scrolled the transcript instead of navigating to the previous submitted message. Typing even one character and pressing Up correctly navigated history — but an empty composer did something completely different.

Issue 2 (#1721): In a multiline composer, Up/Down should navigate within the input text (moving cursor between lines), not immediately jump to input history.

Fix

Changed default_composer_arrows_scroll_for_platform to return !use_mouse_capture only (removed is_windows ||):

  • Mouse capture ON (Windows Terminal, most terminals) → default false → Up/Down navigates history
  • Mouse capture OFF (CMD.exe) → default true → scrolls transcript (mouse-wheel fallback)

The multiline cursor navigation (vim_move_up/vim_move_down) was already working correctly — the second fix ensures the boundary fallback to history only happens at the first/last line.

Changes

File Change
crates/tui/src/tui/app.rs Removed is_windows || from default; updated field doc comment; renamed test
crates/tui/src/tui/ui/tests.rs Renamed platform default test; added 6 new tests

New tests (6)

Test Coverage
history_arrow_down_handles_empty_input Empty composer Up→newest, Down→restores empty draft
history_arrow_down_walks_forward_through_entries 3 entries: Up×3 to oldest, Down×2 walks forward
composer_arrow_up_at_first_line_falls_back_to_history_up Multiline: Up at line 1 falls back to history_up()
composer_arrow_down_at_last_line_falls_back_to_history_down Multiline: Down at last line falls back to history_down()
history_roundtrip_up_then_down_restores_draft Draft → Up to history → Down restores draft + cursor

Related

… history

Replace direct history_up()/history_down() calls with vim_move_up()/
vim_move_down() in handle_composer_history_arrow. These functions
already implement the desired behaviour: move the cursor one logical
line within the composer when the input is multiline (preserving
column position), and fall back to input-history navigation only at
the first or last line.

Previously plain Up/Down always jumped to the previous/next history
entry, making it impossible to edit the line above in a multi-line
prompt without leaving the composer.

Also rename composer_arrows_scroll_nonempty_still_navigates_history
to composer_arrows_scroll_singleline_navigates_history and add two
new tests covering multiline arrow navigation.
…scroll

On terminals with mouse capture (Windows Terminal, most modern terminals),
the composer_arrows_scroll default is now false: empty-composer Up/Down
navigates input history instead of scrolling the transcript.

Previously default_composer_arrows_scroll_for_platform returned
is_windows || !use_mouse_capture, forcing true on all Windows machines.
Now it returns only !use_mouse_capture, so:
- Mouse capture ON  -> default false -> Up/Down navigates history
- Mouse capture OFF -> default true  -> Up/Down scrolls transcript
  (for terminals that map mouse-wheel events to arrow keys, e.g. CMD.exe)

Added 6 tests for the composer arrow + history navigation matrix:
- Empty composer Down restores draft (arrows-scroll=false)
- Down walks forward through multiple history entries
- Up at first line of multiline input falls back to history_up
- Down at last line of multiline input falls back to history_down
- Up-then-Down round-trip restores draft with cursor position

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request updates the TUI composer's arrow key behavior to support multiline navigation and standardizes the composer_arrows_scroll default across platforms. Specifically, it replaces direct history navigation with vim_move_up and vim_move_down, allowing users to move between lines within the composer before falling back to history. Feedback was provided to refine the scroll_on_empty heuristic, as the current implementation using trim().is_empty() would incorrectly trigger transcript scrolling for whitespace-only multiline inputs instead of allowing line navigation.

Comment thread crates/tui/src/tui/composer_ui.rs Outdated
…tespace

Replace `trim().is_empty()` with a check that treats multiline inputs
(those containing '\n') as non-empty, even when whitespace-only.

Before: `"   \n   ".trim()` → `""` → scroll_on_empty = true → scrolls
After:  `"   \n   ".contains('\n')` → true → scroll_on_empty = false → line nav

Single-line whitespace (`"   "`) is still treated as empty, preserving
the existing behavior tested in history_arrow_handles_whitespace_input.
@aboimpinto

Copy link
Copy Markdown
Contributor Author

Good catch — trim().is_empty() was too aggressive. A whitespace-only multiline input like " \n " would collapse to "" via trim(), triggering transcript scroll instead of line navigation.

Fixed in b0c7acd: the heuristic now only treats the composer as "empty" when it's truly empty or contains only single-line whitespace. If \n is present, scroll_on_empty stays false — the user can navigate between lines regardless of content.

let scroll_on_empty = app.composer_arrows_scroll
    && (app.input.is_empty()
        || (!app.input.contains('\n') && app.input.trim().is_empty()));

Added a test (composer_arrows_scroll_multiline_whitespace_navigates_lines) that verifies this case.

@Hmbown

Hmbown commented May 17, 2026

Copy link
Copy Markdown
Owner

Thanks for the detailed composer work. The multiline arrow-navigation fix and useful tests were harvested into v0.8.39 via #1734 and credited in the changelog, so I am closing this PR to keep the queue clean. The broader empty-composer Windows default remains open as #1720 under v0.8.40, where we want a full Windows terminal/mouse-capture matrix before changing defaults again.

@Hmbown Hmbown closed this May 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants