mirror of
https://github.com/l0ng-ai/tty7.git
synced 2026-09-21 16:02:20 +00:00
fix(terminal): adopt the held line at the editor's own doors, not in render
The previous commit folded the seed in from `render`, which never runs in these tests — all four handover tests failed on `never settled`, and in the app a keystroke that beat the frame would have been edited against a line that was still missing its front. `handle_editor_key` and `commit_text` are the two doors into the editor, and both now open with `adopt_typeahead`, so the line is whole before anything reads or rewrites it. `render` goes back to what it was.
This commit is contained in:
+54
-23
@@ -2256,6 +2256,7 @@ impl TerminalView {
|
||||
let key = ks.key.as_str();
|
||||
self.cursor_visible = true;
|
||||
self.jump_to_prompt();
|
||||
self.adopt_typeahead();
|
||||
|
||||
let aliased;
|
||||
let ks = if m.control && !m.platform && !m.alt && matches!(key, "p" | "n") {
|
||||
@@ -3945,16 +3946,22 @@ impl TerminalView {
|
||||
}
|
||||
}
|
||||
|
||||
/// Take the record into the editor without paying the wipe yet.
|
||||
/// Take the record into the editor without paying the wipe yet. Every door
|
||||
/// into the editor opens with this.
|
||||
///
|
||||
/// `at_prompt` comes back on the `D` mark, a whole prompt draw ahead of the
|
||||
/// `B` that arms `zle_reading`, and this editor is live for that whole
|
||||
/// window. Everything it offers rewrites the line — history recall and the
|
||||
/// ghost suggestion replace it wholesale, ⌃U empties it, completion filters
|
||||
/// on it — so the line has to be whole *before* those run, not stitched
|
||||
/// back together at submit time in front of whatever replaced it. The `^U`
|
||||
/// stays owed until `flush_typeahead`, which keeps it where it has always
|
||||
/// been on the wire: immediately before the line.
|
||||
/// back together at submit time in front of whatever replaced it. Folding
|
||||
/// it in that late made `↑` then Enter run the recalled entry with the gap
|
||||
/// text glued to its front, and ⌃U then Enter bring back the text ⌃U had
|
||||
/// just cleared.
|
||||
///
|
||||
/// The `^U` stays owed until `flush_typeahead`, which keeps it where it has
|
||||
/// always been on the wire: immediately before the line. Sending it here
|
||||
/// instead would put it out before the shell's own editor is reading.
|
||||
fn adopt_typeahead(&mut self) {
|
||||
if let Some(seed) = self.typeahead.adopt() {
|
||||
self.cmd.prepend_str(&seed);
|
||||
@@ -4072,12 +4079,12 @@ impl TerminalView {
|
||||
// The shell is still holding the recorded text on its own line, and the
|
||||
// ^U that erases it has not gone out yet: `at_prompt` comes back on the
|
||||
// `D` mark, before the prompt is even drawn, while the wipe waits for
|
||||
// `B`. `adopt_typeahead` has normally already folded the seed into the
|
||||
// line by now, and this pays the wipe it left owed; on the frame where
|
||||
// it has not, the drain here still puts the seed back the way every
|
||||
// other drain does. Dropping it submitted only what was typed after the
|
||||
// handover, and an empty command when that was nothing, which is the
|
||||
// blank line #433 reports.
|
||||
// `B`. The key that got here has folded the seed into the line already,
|
||||
// so this is usually just paying the wipe that left owed; where nothing
|
||||
// has, the drain still puts the seed back the way every other drain
|
||||
// does. Dropping it submitted only what was typed after the handover,
|
||||
// and an empty command when that was nothing, which is the blank line
|
||||
// #433 reports.
|
||||
self.flush_typeahead();
|
||||
let line = self.cmd.text();
|
||||
if !line.trim().is_empty() {
|
||||
@@ -4836,6 +4843,7 @@ impl TerminalView {
|
||||
return;
|
||||
}
|
||||
if self.input_active() {
|
||||
self.adopt_typeahead();
|
||||
self.cmd.insert_str(text);
|
||||
self.history_nav = None;
|
||||
self.editor_goal_col = None;
|
||||
@@ -6360,8 +6368,6 @@ impl Render for TerminalView {
|
||||
}
|
||||
if self.terminal.zle_reading() {
|
||||
self.flush_typeahead();
|
||||
} else {
|
||||
self.adopt_typeahead();
|
||||
}
|
||||
}
|
||||
let entity = cx.entity();
|
||||
@@ -14054,17 +14060,6 @@ mod prompt_handover_tests {
|
||||
.unwrap(),
|
||||
"this is the D-to-B window: the shell is not reading its line yet"
|
||||
);
|
||||
settle(
|
||||
cx,
|
||||
window,
|
||||
"the editor adopts the line the shell is holding",
|
||||
|view| view.cmd.text() == text,
|
||||
);
|
||||
assert_eq!(
|
||||
drain(daemon),
|
||||
Vec::<u8>::new(),
|
||||
"adopting the line owes the wipe, it does not send it early"
|
||||
);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
@@ -14107,6 +14102,42 @@ mod prompt_handover_tests {
|
||||
);
|
||||
}
|
||||
|
||||
/// The editor takes the held line over the moment it is touched, before it
|
||||
/// edits anything — and takes it over without putting the wipe on the wire,
|
||||
/// which is still the shell's line editor's to receive when it starts
|
||||
/// reading.
|
||||
#[gpui::test]
|
||||
fn the_editor_takes_the_held_line_over_before_it_edits_it(cx: &mut TestAppContext) {
|
||||
let (window, mut daemon) = harness(cx);
|
||||
typed_into_the_gap_then_handed_back(cx, &window, &mut daemon, "echo");
|
||||
|
||||
// `home` moves the caret and nothing else: any editor key is enough.
|
||||
press(&window, cx, "home");
|
||||
cx.run_until_parked();
|
||||
window
|
||||
.update(cx, |view, _, _| {
|
||||
assert_eq!(
|
||||
view.cmd.text(),
|
||||
"echo",
|
||||
"the line the shell is sitting on is the editor's line now"
|
||||
);
|
||||
})
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
drain(&mut daemon),
|
||||
Vec::<u8>::new(),
|
||||
"taking the line over owes the wipe, it does not send it early"
|
||||
);
|
||||
|
||||
press(&window, cx, "enter");
|
||||
cx.run_until_parked();
|
||||
assert_eq!(
|
||||
drain(&mut daemon),
|
||||
b"\x15echo\r".to_vec(),
|
||||
"the owed wipe is paid on submit, still in front of the line"
|
||||
);
|
||||
}
|
||||
|
||||
/// The half of the window the seed alone does not cover: the editor is
|
||||
/// live, so the user can *replace* the line before submitting it. Recalling
|
||||
/// history and pressing Enter has to run the entry recalled — not that
|
||||
|
||||
Reference in New Issue
Block a user