mirror of
https://github.com/fish-shell/fish-shell
synced 2024-12-25 12:23:09 +00:00
On undo after execute, restore the cursor position
Ever since 149594f974
(Initial revision, 2005-09-20), we move the
cursor to the end of the commandline just before executing it.
This is so we can move the cursor to the line below the command line,
so moving the cursor is relevant if one presses enter on say, the
first line of a multi-line commandline.
As mentioned in #10838 and others, it can be useful to restore the
cursor position when recalling commandline from history. Make undo
restore the position where enter was pressed, instead of implicitly
moving the cursor to the end. This allows to quickly correct small
mistakes in large commandlines that failed recently.
This requires a new way of moving the cursor below the command line.
Test changes include unrelated cleanup of history.py.
This commit is contained in:
parent
f9fb026085
commit
610338cc70
10 changed files with 87 additions and 49 deletions
|
@ -1,3 +1,36 @@
|
|||
fish 4.1.0 (released ???)
|
||||
=========================
|
||||
|
||||
Notable improvements and fixes
|
||||
------------------------------
|
||||
|
||||
Deprecations and removed features
|
||||
---------------------------------
|
||||
|
||||
Scripting improvements
|
||||
----------------------
|
||||
|
||||
Interactive improvements
|
||||
------------------------
|
||||
|
||||
New or improved bindings
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
- :kbd:`ctrl-z` (undo) after executing a command will restore the previous cursor position instead of placing the cursor at the end of the command line.
|
||||
|
||||
Completions
|
||||
^^^^^^^^^^^
|
||||
|
||||
Improved terminal support
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Other improvements
|
||||
------------------
|
||||
|
||||
For distributors
|
||||
----------------
|
||||
|
||||
--------------
|
||||
|
||||
fish 4.0b1 (released December 17, 2024)
|
||||
=======================================
|
||||
|
||||
|
|
|
@ -129,6 +129,10 @@ 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.
|
||||
|
|
|
@ -1972,11 +1972,8 @@ impl<'a> Reader<'a> {
|
|||
zelf.finish_highlighting_before_exec();
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
// Move the cursor so that output is on the line after the command.
|
||||
zelf.screen.move_to_end();
|
||||
|
||||
// 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.
|
||||
|
@ -3582,7 +3579,6 @@ impl<'a> Reader<'a> {
|
|||
|
||||
self.add_to_history();
|
||||
self.rls_mut().finished = true;
|
||||
self.update_buff_pos(elt, Some(self.command_line_len()));
|
||||
true
|
||||
}
|
||||
|
||||
|
|
|
@ -473,6 +473,10 @@ impl Screen {
|
|||
self.save_status();
|
||||
}
|
||||
|
||||
pub fn move_to_end(&mut self) {
|
||||
self.r#move(0, self.actual.line_count());
|
||||
}
|
||||
|
||||
/// 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,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#!/usr/bin/env python3
|
||||
from pexpect_helper import SpawnedProc
|
||||
from pexpect_helper import SpawnedProc, TO_END
|
||||
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("\r\n.*def abc\r\n") # emacs transpose words, default timeout: no delay
|
||||
expect_prompt(TO_END + "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("\r\n.*jkl ghi\r\n")
|
||||
expect_prompt(TO_END + "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("\r\n.*mno pqrt\r\n")
|
||||
expect_prompt(TO_END + "mno pqrt\r\n")
|
||||
|
||||
# Now test that exactly the expected bind modes are defined
|
||||
sendline("bind --list-modes")
|
||||
expect_prompt("\r\n.*default", unmatched="Unexpected bind modes")
|
||||
expect_prompt(TO_END + "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(
|
||||
"\r\n.*success: default escape timeout", unmatched="prime vi mode, default timeout"
|
||||
TO_END + "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(
|
||||
"\r\n.*success: default escape timeout\r\n",
|
||||
TO_END + "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(
|
||||
"\r\n.*TAXT\r\n", unmatched="vi mode replace char, default timeout: long delay"
|
||||
TO_END + "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(
|
||||
"\r\n.*MORE\r\n", unmatched="vi mode delete char, default timeout: long delay"
|
||||
TO_END + "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(
|
||||
"\r\n.*MORE\r\n",
|
||||
TO_END + "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(
|
||||
# "\r\n.*MORE-TEXT-IS\r\n",
|
||||
# TO_END + "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(
|
||||
"\r\n.*MORE-TEXT\r\n",
|
||||
TO_END + "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(
|
||||
"\r\n.*MORE-TEXT-IS\r\n",
|
||||
TO_END + "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(
|
||||
"\r\n.*success: lengthened escape timeout\r\n",
|
||||
TO_END + "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(
|
||||
"\r\n.*fail: no normal modediinserted\r\n",
|
||||
TO_END + "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("\r\n.*TENT\r\n", unmatched="Couldn't find expected output 'TENT'")
|
||||
expect_prompt(TO_END + "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("\r\n.*SOME TeXT\r\n", unmatched="Couldn't find expected output 'SOME TeXT")
|
||||
# expect_prompt(TO_END + "SOME TeXT\r\n", unmatched="Couldn't find expected output 'SOME TeXT")
|
||||
|
||||
# Now test that exactly the expected bind modes are defined
|
||||
sendline("bind --list-modes")
|
||||
|
@ -255,7 +255,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(
|
||||
"\r\n.*fish_escape_delay_ms=50\r\n",
|
||||
TO_END + "fish_escape_delay_ms=50\r\n",
|
||||
unmatched="default-mode custom timeout not set correctly",
|
||||
)
|
||||
|
||||
|
@ -270,7 +270,7 @@ send("echo abc def")
|
|||
send("\033")
|
||||
send("t\r")
|
||||
expect_prompt(
|
||||
"\r\n.*def abc\r\n", unmatched="emacs transpose words fail, 200ms timeout: no delay"
|
||||
TO_END + "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
|
||||
|
@ -301,7 +301,7 @@ expect_prompt()
|
|||
send("foo ")
|
||||
expect_str("echo foonanana")
|
||||
send(" banana\r")
|
||||
expect_str(" banana\r")
|
||||
expect_str(" banana")
|
||||
expect_prompt("foonanana banana")
|
||||
|
||||
# Ensure that nul can be bound properly (#3189).
|
||||
|
@ -329,7 +329,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("\n.*b c d")
|
||||
expect_prompt(TO_END + "b c d")
|
||||
|
||||
# Test that overriding the escape binding works
|
||||
# and does not inhibit other escape sequences (up-arrow in this case).
|
||||
|
@ -345,7 +345,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("\n.*b c d")
|
||||
expect_prompt(TO_END + "b c d")
|
||||
|
||||
# Check that ctrl-z can be bound
|
||||
sendline('bind ctrl-z "echo bound ctrl-z"')
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#!/usr/bin/env python3
|
||||
from pexpect_helper import SpawnedProc
|
||||
from pexpect_helper import SpawnedProc, TO_END
|
||||
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(f"\r\n.*ready to go\r\n")
|
||||
expect_prompt(TO_END + f"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("\r\n.*mode changes: default insert default insert\r\n")
|
||||
expect_prompt(TO_END + "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("\r\n.*mode changes: default insert\r\n")
|
||||
expect_prompt(TO_END + "mode changes: default insert\r\n")
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#!/usr/bin/env python3
|
||||
from pexpect_helper import SpawnedProc
|
||||
from pexpect_helper import SpawnedProc, TO_END
|
||||
|
||||
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("\n.*bar")
|
||||
expect_re(TO_END + "bar")
|
||||
sendline("echo fo\t")
|
||||
expect_re("foooo")
|
||||
|
|
|
@ -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
|
||||
from pexpect_helper import SpawnedProc, TO_END, TO_END_SUFFIX
|
||||
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'\r\n")
|
||||
expect_prompt("history delete -e -C 'echo hello'" + TO_END_SUFFIX)
|
||||
sendline("echo count hello (history search -e -C 'echo hello' | wc -l | string trim)")
|
||||
expect_prompt("count hello 0\r\n")
|
||||
|
||||
|
@ -107,12 +107,13 @@ 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'\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("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("Delete which entries\? ")
|
||||
sendline("1")
|
||||
expect_prompt('Deleting history entry 1: "echo hello AGAIN"\r\n')
|
||||
|
@ -177,7 +178,7 @@ expect_prompt()
|
|||
sendline("history clear-session")
|
||||
expect_prompt()
|
||||
sendline("history search --exact 'echo after' | cat")
|
||||
expect_prompt("\r\n")
|
||||
expect_prompt()
|
||||
|
||||
# Check history filtering
|
||||
# We store anything that starts with "echo ephemeral".
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#!/usr/bin/env python3
|
||||
from pexpect_helper import SpawnedProc
|
||||
from pexpect_helper import SpawnedProc, TO_END
|
||||
|
||||
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("\r\n.*@MARKER:" + str(text) + "@\\r\\n")
|
||||
expect_prompt(TO_END + "@MARKER:" + str(text) + "@\\r\\n")
|
||||
|
||||
|
||||
def print_var_contents(varname, expected):
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#!/usr/bin/env python3
|
||||
from pexpect_helper import SpawnedProc
|
||||
from pexpect_helper import SpawnedProc, TO_END
|
||||
|
||||
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("\r\n.*status current-commandline\r\n")
|
||||
expect_prompt(TO_END + "status current-commandline\r\n")
|
||||
|
||||
# Validate behavior as part of a command chain
|
||||
sendline("true 7 && status current-commandline")
|
||||
expect_prompt("\r\n.*true 7 && status current-commandline\r\n")
|
||||
expect_prompt(TO_END + "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("\r\n.*report 27\r\n")
|
||||
expect_prompt(TO_END + "report 27\r\n")
|
||||
|
||||
# Exit
|
||||
send("\x04") # <c-d>
|
||||
|
|
Loading…
Reference in a new issue