fix(terminal): bring the view back to the prompt when you start typing

Scroll up into the scrollback and type: the characters went to the shell,
the prompt line grew, and the viewport did not move. Nothing on screen
changed, so the pane read as having stopped listening — while it was in
fact accepting every keystroke somewhere the user could not see.

`commit_text` has three branches and only the last one jumped to the
prompt: the one taken when tty7 is *not* driving the line. At a shell
prompt with OSC 133 reporting — the normal case, and the one the editor
features exist for — the text goes into `cmd` and returned early. Same
for a reverse-search query. `handle_editor_key` has always jumped, so the
two halves of the same editor disagreed: Left and Backspace came back to
the prompt and the letters between them did not.

Hoisted the jump above all three branches, where it describes the whole
function: text typed goes to the prompt, so the view has to be looking at
the prompt. Held by a test that fails on the old order.

Verified in a dev window: 200 lines printed, scrolled 15 notches back,
typed — the grid snaps to the prompt with the characters on it.
This commit is contained in:
l0ng-ai
2026-08-09 10:07:09 +07:00
parent a8c2d74ebf
commit fa8f7cee31
+32 -1
View File
@@ -3964,6 +3964,14 @@ impl TerminalView {
if self.terminal.exited || text.is_empty() || !self.accepts_input(cx) {
return;
}
// Typing goes to the prompt, so the view has to be looking at it.
// Only the last branch below used to do this, which is the branch
// taken when tty7 is *not* driving the line — so scrolling up and
// typing did the one thing it must never do at a shell prompt:
// accepted the characters somewhere the user could not see them.
// `handle_editor_key` has always jumped, so Left and Backspace came
// back to the prompt and the letters between them did not.
self.jump_to_prompt();
if let Some(rs) = self.reverse_search.as_mut() {
rs.push_query(text, &self.history, &self.history_frecency);
self.cursor_visible = true;
@@ -3982,7 +3990,6 @@ impl TerminalView {
}
self.write_gap_text(text, text.as_bytes().to_vec(), cx);
self.cursor_visible = true;
self.jump_to_prompt();
cx.notify();
}
@@ -8590,6 +8597,30 @@ mod gpui_tests {
.unwrap();
}
/// The one thing typing at a prompt must never do is land where the
/// person typing cannot see it.
#[gpui::test]
fn typing_brings_the_view_back_to_the_prompt(cx: &mut TestAppContext) {
let (window, mut daemon) = harness(cx);
prompt_ready(&window, cx, &mut daemon);
window
.update(cx, |view, _w, cx| {
assert!(
view.input_active(),
"this is the branch that was leaving the view parked"
);
scroll_into_history(view, 10);
view.commit_text("l", cx);
assert_eq!(
display_offset(view),
0,
"the character went in while the viewport stayed in the scrollback"
);
assert_eq!(view.cmd.text(), "l", "and it did reach the line");
})
.unwrap();
}
/// A trackpad is already a continuous stream — animating it would only put
/// lag between the fingers and the grid. It is told apart by its phase, not
/// by its delta type or size: a flick moves further in one event than a