From c9f1979b057477a43aa141e656b94132cea8a964 Mon Sep 17 00:00:00 2001 From: Peter Ammon Date: Wed, 5 Mar 2025 15:54:20 -0800 Subject: [PATCH] Revert "On undo after execute, restore the cursor position" This reverts commit 815bc054e73d53ffaba13949cd6017cecba7f792. This is too ambitious for a patch release, given that it affects how every typed-in command runs. --- build_tools/pexpect_helper.py | 4 --- src/reader.rs | 12 ++++---- src/screen.rs | 24 +++++----------- tests/pexpects/bind.py | 44 +++++++++++++++--------------- tests/pexpects/bind_mode_events.py | 8 +++--- tests/pexpects/complete.py | 4 +-- tests/pexpects/history.py | 19 ++++++------- tests/pexpects/read.py | 4 +-- tests/pexpects/status.py | 8 +++--- 9 files changed, 57 insertions(+), 70 deletions(-) diff --git a/build_tools/pexpect_helper.py b/build_tools/pexpect_helper.py index e8bb143e4..347c699f5 100644 --- a/build_tools/pexpect_helper.py +++ b/build_tools/pexpect_helper.py @@ -129,10 +129,6 @@ class Message(object): """Return a output message with the given text.""" return Message(Message.DIR_OUTPUT, text, when) -# Sequences for moving the cursor below the commandline. This happens before executing. -MOVE_TO_END: str = r"(?:\r\n|\x1b\[2 q)" -TO_END: str = MOVE_TO_END + r"[^\n]*" -TO_END_SUFFIX: str = r"[^\n]*" + MOVE_TO_END class SpawnedProc(object): """A process, talking to our ptty. This wraps pexpect.spawn. diff --git a/src/reader.rs b/src/reader.rs index 8d169b637..fcdecbcde 100644 --- a/src/reader.rs +++ b/src/reader.rs @@ -1988,9 +1988,7 @@ fn readline(&mut self, nchars: Option) -> Option { // Redraw the command line. This is what ensures the autosuggestion is hidden, etc. after the // user presses enter. - if zelf.is_repaint_needed(None) - || zelf.screen.scrolled() - || zelf.conf.inputfd != STDIN_FILENO + if zelf.is_repaint_needed(None) || zelf.screen.scrolled || zelf.conf.inputfd != STDIN_FILENO { zelf.layout_and_repaint_before_execution(); } @@ -2000,8 +1998,11 @@ fn readline(&mut self, nchars: Option) -> Option { zelf.finish_highlighting_before_exec(); } - // Move the cursor so that output is on the line after the command. - zelf.screen.move_to_end(); + // Emit a newline so that the output is on the line after the command. + // But do not emit a newline if the cursor has wrapped onto a new line all its own - see #6826. + if !zelf.screen.cursor_is_wrapped_to_own_line() { + let _ = write_to_fd(b"\n", STDOUT_FILENO); + } // HACK: If stdin isn't the same terminal as stdout, we just moved the cursor. // For now, just reset it to the beginning of the line. @@ -3643,6 +3644,7 @@ fn handle_execute(&mut self) -> bool { self.add_to_history(); self.rls_mut().finished = true; + self.update_buff_pos(elt, Some(self.command_line_len())); true } diff --git a/src/screen.rs b/src/screen.rs index e4ddfa077..479622198 100644 --- a/src/screen.rs +++ b/src/screen.rs @@ -177,9 +177,8 @@ pub fn is_empty(&self) -> bool { pub struct Screen { /// Whether the last-drawn autosuggestion (if any) is truncated, or hidden entirely. pub autosuggestion_is_truncated: bool, - /// If the last rendering was so large we could only display part of the command line, - /// this is the number of lines that were pushed to scrollback. - pub scroll_amount: usize, + /// True if the last rendering was so large we could only display part of the command line. + pub scrolled: bool, /// Receiver for our output. outp: &'static RefCell, @@ -216,7 +215,7 @@ pub fn new() -> Self { Self { outp: Outputter::stdoutput(), autosuggestion_is_truncated: Default::default(), - scroll_amount: Default::default(), + scrolled: Default::default(), desired: Default::default(), actual: Default::default(), actual_left_prompt: Default::default(), @@ -230,10 +229,6 @@ pub fn new() -> Self { } } - pub fn scrolled(&self) -> bool { - self.scroll_amount != 0 - } - /// This is the main function for the screen output library. It is used to define the desired /// contents of the screen. The screen command will use its knowledge of the current contents of /// the screen in order to render the desired output using as few terminal commands as possible. @@ -438,7 +433,7 @@ struct ScrolledCursor { // Append pager_data (none if empty). self.desired.append_lines(&page_rendering.screen_data); - self.scroll_amount = scrolled_cursor.scroll_amount; + self.scrolled = scrolled_cursor.scroll_amount != 0; self.update( vars, @@ -478,10 +473,6 @@ pub fn reset_line(&mut self, repaint_prompt: bool /* = false */) { self.save_status(); } - pub fn move_to_end(&mut self) { - self.r#move(0, self.actual.line_count() - self.scroll_amount); - } - /// Resets the screen buffer's internal knowledge about the contents of the screen, /// abandoning the current line and going to the next line. /// If clear_to_eos is set, @@ -930,15 +921,14 @@ fn update( let term = term.as_ref(); // Output the left prompt if it has changed. - if zelf.scrolled() && !is_final_rendering { + if zelf.scrolled && !is_final_rendering { zelf.r#move(0, 0); zelf.outp .borrow_mut() .tputs_if_some(&term.and_then(|term| term.clr_eol.as_ref())); zelf.actual_left_prompt.clear(); zelf.actual.cursor.x = 0; - } else if left_prompt != zelf.actual_left_prompt || (zelf.scrolled() && is_final_rendering) - { + } else if left_prompt != zelf.actual_left_prompt || (zelf.scrolled && is_final_rendering) { zelf.r#move(0, 0); let mut start = 0; let osc_133_prompt_start = @@ -988,7 +978,7 @@ fn s_line(zelf: &Screen, i: usize) -> &Line { // Note that skip_remaining is a width, not a character count. let mut skip_remaining = start_pos; - let shared_prefix = if zelf.scrolled() { + let shared_prefix = if zelf.scrolled { 0 } else { line_shared_prefix(o_line(&zelf, i), s_line(&zelf, i)) diff --git a/tests/pexpects/bind.py b/tests/pexpects/bind.py index 565223135..fdbe9b629 100644 --- a/tests/pexpects/bind.py +++ b/tests/pexpects/bind.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -from pexpect_helper import SpawnedProc, TO_END +from pexpect_helper import SpawnedProc import os import platform import sys @@ -42,7 +42,7 @@ expect_prompt("") # Start by testing with no delay. This should transpose the words. send("echo abc def") send("\033t\r") -expect_prompt(TO_END + "def abc\r\n") # emacs transpose words, default timeout: no delay +expect_prompt("\r\n.*def abc\r\n") # emacs transpose words, default timeout: no delay # Now test with a delay > 0 and < the escape timeout. This should transpose # the words. @@ -51,7 +51,7 @@ send("\033") sleep(0.010) send("t\r") # emacs transpose words, default timeout: short delay -expect_prompt(TO_END + "jkl ghi\r\n") +expect_prompt("\r\n.*jkl ghi\r\n") # Now test with a delay > the escape timeout. The transposition should not # occur and the "t" should become part of the text that is echoed. @@ -60,11 +60,11 @@ send("\033") sleep(0.250) send("t\r") # emacs transpose words, default timeout: long delay -expect_prompt(TO_END + "mno pqrt\r\n") +expect_prompt("\r\n.*mno pqrt\r\n") # Now test that exactly the expected bind modes are defined sendline("bind --list-modes") -expect_prompt(TO_END + "default", unmatched="Unexpected bind modes") +expect_prompt("\r\n.*default", unmatched="Unexpected bind modes") # Test vi key bindings. # This should leave vi mode in the insert state. @@ -74,7 +74,7 @@ expect_prompt() # Go through a prompt cycle to let fish catch up, it may be slow due to ASAN sendline("echo success: default escape timeout") expect_prompt( - TO_END + "success: default escape timeout", unmatched="prime vi mode, default timeout" + "\r\n.*success: default escape timeout", unmatched="prime vi mode, default timeout" ) send("echo fail: default escape timeout") @@ -88,7 +88,7 @@ sleep(0.250) send("ddi") sendline("echo success: default escape timeout") expect_prompt( - TO_END + "success: default escape timeout\r\n", + "\r\n.*success: default escape timeout\r\n", unmatched="vi replace line, default timeout: long delay", ) @@ -103,7 +103,7 @@ send("\033") sleep(0.400) send("hhrAi\r") expect_prompt( - TO_END + "TAXT\r\n", unmatched="vi mode replace char, default timeout: long delay" + "\r\n.*TAXT\r\n", unmatched="vi mode replace char, default timeout: long delay" ) # Test deleting characters with 'x'. @@ -115,7 +115,7 @@ send("xxxxx\r") # vi mode delete char, default timeout: long delay expect_prompt( - TO_END + "MORE\r\n", unmatched="vi mode delete char, default timeout: long delay" + "\r\n.*MORE\r\n", unmatched="vi mode delete char, default timeout: long delay" ) # Test jumping forward til before a character with t @@ -127,7 +127,7 @@ send("0tTD\r") # vi mode forward-jump-till character, default timeout: long delay expect_prompt( - TO_END + "MORE\r\n", + "\r\n.*MORE\r\n", unmatched="vi mode forward-jump-till character, default timeout: long delay", ) @@ -140,7 +140,7 @@ expect_prompt( # send("TSD\r") # # vi mode backward-jump-till character, default timeout: long delay # expect_prompt( -# TO_END + "MORE-TEXT-IS\r\n", +# "\r\n.*MORE-TEXT-IS\r\n", # unmatched="vi mode backward-jump-till character, default timeout: long delay", # ) @@ -152,7 +152,7 @@ sleep(0.250) send("F-;D\r") # vi mode backward-jump-to character and repeat, default timeout: long delay expect_prompt( - TO_END + "MORE-TEXT\r\n", + "\r\n.*MORE-TEXT\r\n", unmatched="vi mode backward-jump-to character and repeat, default timeout: long delay", ) @@ -164,7 +164,7 @@ sleep(0.250) send("F-F-,D\r") # vi mode backward-jump-to character, and reverse, default timeout: long delay expect_prompt( - TO_END + "MORE-TEXT-IS\r\n", + "\r\n.*MORE-TEXT-IS\r\n", unmatched="vi mode backward-jump-to character, and reverse, default timeout: long delay", ) @@ -179,7 +179,7 @@ send("ddi") sleep(0.25) send("echo success: lengthened escape timeout\r") expect_prompt( - TO_END + "success: lengthened escape timeout\r\n", + "\r\n.*success: lengthened escape timeout\r\n", unmatched="vi replace line, 100ms timeout: long delay", ) @@ -191,7 +191,7 @@ sleep(0.010) send("ddi") send("inserted\r") expect_prompt( - TO_END + "fail: no normal modediinserted\r\n", + "\r\n.*fail: no normal modediinserted\r\n", unmatched="vi replace line, 100ms timeout: short delay", ) @@ -208,7 +208,7 @@ expect_str("echo TEXT") send("\033") sleep(0.200) send("hhtTrN\r") -expect_prompt(TO_END + "TENT\r\n", unmatched="Couldn't find expected output 'TENT'") +expect_prompt("\r\n.*TENT\r\n", unmatched="Couldn't find expected output 'TENT'") # Test sequence key delay send("set -g fish_sequence_key_delay_ms 200\r") @@ -239,7 +239,7 @@ expect_prompt("foo") # send("echo some TExT\033") # sleep(0.300) # send("hh~~bbve~\r") -# expect_prompt(TO_END + "SOME TeXT\r\n", unmatched="Couldn't find expected output 'SOME TeXT") +# expect_prompt("\r\n.*SOME TeXT\r\n", unmatched="Couldn't find expected output 'SOME TeXT") send("echo echo") send("\033") @@ -261,7 +261,7 @@ expect_prompt() # Verify the custom escape timeout set earlier is still in effect. sendline("echo fish_escape_delay_ms=$fish_escape_delay_ms") expect_prompt( - TO_END + "fish_escape_delay_ms=50\r\n", + "\r\n.*fish_escape_delay_ms=50\r\n", unmatched="default-mode custom timeout not set correctly", ) @@ -276,7 +276,7 @@ send("echo abc def") send("\033") send("t\r") expect_prompt( - TO_END + "def abc\r\n", unmatched="emacs transpose words fail, 200ms timeout: no delay" + "\r\n.*def abc\r\n", unmatched="emacs transpose words fail, 200ms timeout: no delay" ) # Verify special characters, such as \cV, are not intercepted by the kernel @@ -307,7 +307,7 @@ expect_prompt() send("foo ") expect_str("echo foonanana") send(" banana\r") -expect_str(" banana") +expect_str(" banana\r") expect_prompt("foonanana banana") # Ensure that nul can be bound properly (#3189). @@ -335,7 +335,7 @@ expect_prompt() send("a b c d\x01") # ctrl-a, move back to the beginning of the line send("\x07") # ctrl-g, kill bigword sendline("echo") -expect_prompt(TO_END + "b c d") +expect_prompt("\n.*b c d") # Test that overriding the escape binding works # and does not inhibit other escape sequences (up-arrow in this case). @@ -351,7 +351,7 @@ expect_prompt() send(" a b c d\x01") # ctrl-a, move back to the beginning of the line send("\x07") # ctrl-g, kill bigword sendline("echo") -expect_prompt(TO_END + "b c d") +expect_prompt("\n.*b c d") # Check that ctrl-z can be bound sendline('bind ctrl-z "echo bound ctrl-z"') diff --git a/tests/pexpects/bind_mode_events.py b/tests/pexpects/bind_mode_events.py index 11db8e078..4ee9c37c2 100644 --- a/tests/pexpects/bind_mode_events.py +++ b/tests/pexpects/bind_mode_events.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -from pexpect_helper import SpawnedProc, TO_END +from pexpect_helper import SpawnedProc import os import sys import signal @@ -16,7 +16,7 @@ send("set -g fish_key_bindings fish_vi_key_bindings\r") expect_prompt() send("echo ready to go\r") -expect_prompt(TO_END + f"ready to go\r\n") +expect_prompt(f"\r\n.*ready to go\r\n") send( "function add_change --on-variable fish_bind_mode ; set -g MODE_CHANGES $MODE_CHANGES $fish_bind_mode ; end\r" ) @@ -42,7 +42,7 @@ send("i") sleep(10 if "CI" in os.environ else 1) send("echo mode changes: $MODE_CHANGES\r") -expect_prompt(TO_END + "mode changes: default insert default insert\r\n") +expect_prompt("\r\n.*mode changes: default insert default insert\r\n") # Regression test for #8125. # Control-C should return us to insert mode. @@ -70,4 +70,4 @@ sleep(timeout) # We should be back in insert mode now. send("echo mode changes: $MODE_CHANGES\r") -expect_prompt(TO_END + "mode changes: default insert\r\n") +expect_prompt("\r\n.*mode changes: default insert\r\n") diff --git a/tests/pexpects/complete.py b/tests/pexpects/complete.py index 2f4f898a4..843d4d192 100644 --- a/tests/pexpects/complete.py +++ b/tests/pexpects/complete.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -from pexpect_helper import SpawnedProc, TO_END +from pexpect_helper import SpawnedProc sp = SpawnedProc() send, sendline, sleep, expect_prompt, expect_re, expect_str = ( @@ -75,6 +75,6 @@ send("echo fo\t") expect_re("foooo") send("\x07") sendline("echo bar") -expect_re(TO_END + "bar") +expect_re("\n.*bar") sendline("echo fo\t") expect_re("foooo") diff --git a/tests/pexpects/history.py b/tests/pexpects/history.py index d8612cfb8..0e931b1f0 100644 --- a/tests/pexpects/history.py +++ b/tests/pexpects/history.py @@ -11,7 +11,7 @@ # The history function might pipe output through the user's pager. We don't # want something like `less` to complicate matters so force the use of `cat`. -from pexpect_helper import SpawnedProc, TO_END, TO_END_SUFFIX +from pexpect_helper import SpawnedProc import os os.environ["PAGER"] = "cat" @@ -98,7 +98,7 @@ expect_prompt("echo start1; builtin history; echo end1\r\n") # ========== # Delete a single command we recently ran. sendline("history delete -e -C 'echo hello'") -expect_prompt("history delete -e -C 'echo hello'" + TO_END_SUFFIX) +expect_prompt("history delete -e -C 'echo hello'\r\n") sendline("echo count hello (history search -e -C 'echo hello' | wc -l | string trim)") expect_prompt("count hello 0\r\n") @@ -107,13 +107,12 @@ expect_prompt("count hello 0\r\n") # delete the first entry matched by the prefix search (the most recent command # sent above that matches). sendline("history delete -p 'echo hello'") -expect_re("history delete -p 'echo hello'" + TO_END_SUFFIX) -expect_re("\[1\] echo hello AGAIN" + TO_END_SUFFIX) -expect_re("\[2\] echo hello again" + TO_END_SUFFIX) -expect_re("Enter nothing to cancel the delete, or\r\n") -expect_re("Enter one or more of the entry IDs or ranges like '5..12', separated by a space.\r\n") -expect_re("For example '7 10..15 35 788..812'.\r\n") -expect_re("Enter 'all' to delete all the matching entries.\r\n") +expect_re("history delete -p 'echo hello'\r\n") +expect_re("\[1\] echo hello AGAIN\r\n") +expect_re("\[2\] echo hello again\r\n\r\n") +expect_re( + "Enter nothing to cancel the delete, or\r\nEnter one or more of the entry IDs or ranges like '5..12', separated by a space.\r\nFor example '7 10..15 35 788..812'.\r\nEnter 'all' to delete all the matching entries.\r\n" +) expect_re("Delete which entries\? ") sendline("1") expect_prompt('Deleting history entry 1: "echo hello AGAIN"\r\n') @@ -178,7 +177,7 @@ expect_prompt() sendline("history clear-session") expect_prompt() sendline("history search --exact 'echo after' | cat") -expect_prompt() +expect_prompt("\r\n") # Check history filtering # We store anything that starts with "echo ephemeral". diff --git a/tests/pexpects/read.py b/tests/pexpects/read.py index a831918b3..4eabb531e 100644 --- a/tests/pexpects/read.py +++ b/tests/pexpects/read.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -from pexpect_helper import SpawnedProc, TO_END +from pexpect_helper import SpawnedProc sp = SpawnedProc() send, sendline, sleep, expect_prompt, expect_re, expect_str = ( @@ -17,7 +17,7 @@ def expect_read_prompt(): def expect_marker(text): - expect_prompt(TO_END + "@MARKER:" + str(text) + "@\\r\\n") + expect_prompt("\r\n.*@MARKER:" + str(text) + "@\\r\\n") def print_var_contents(varname, expected): diff --git a/tests/pexpects/status.py b/tests/pexpects/status.py index b805d7a6d..01633395a 100644 --- a/tests/pexpects/status.py +++ b/tests/pexpects/status.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -from pexpect_helper import SpawnedProc, TO_END +from pexpect_helper import SpawnedProc sp = SpawnedProc() send, sendline, sleep, expect_prompt, expect_re, expect_str = ( @@ -22,11 +22,11 @@ expect_prompt("") # Validate standalone behavior sendline("status current-commandline") -expect_prompt(TO_END + "status current-commandline\r\n") +expect_prompt("\r\n.*status current-commandline\r\n") # Validate behavior as part of a command chain sendline("true 7 && status current-commandline") -expect_prompt(TO_END + "true 7 && status current-commandline\r\n") +expect_prompt("\r\n.*true 7 && status current-commandline\r\n") # Validate behavior when used in a function sendline("function report; set -g last_cmdline (status current-commandline); end") @@ -34,7 +34,7 @@ expect_prompt("") sendline("report 27") expect_prompt("") sendline("echo $last_cmdline") -expect_prompt(TO_END + "report 27\r\n") +expect_prompt("\r\n.*report 27\r\n") # Exit send("\x04") #