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.

(cherry picked from commit 610338cc70)
(cherry picked from commit 0e512f8033)
This commit is contained in:
Johannes Altmanninger
2024-12-21 10:27:52 +01:00
parent b8af4b20c2
commit 815bc054e7
9 changed files with 70 additions and 57 deletions

View File

@@ -1988,7 +1988,9 @@ fn readline(&mut self, nchars: Option<NonZeroUsize>) -> Option<WString> {
// 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();
}
@@ -1998,11 +2000,8 @@ fn readline(&mut self, nchars: Option<NonZeroUsize>) -> Option<WString> {
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.
@@ -3644,7 +3643,6 @@ 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
}

View File

@@ -177,8 +177,9 @@ 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,
/// True if the last rendering was so large we could only display part of the command line.
pub scrolled: 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,
/// Receiver for our output.
outp: &'static RefCell<Outputter>,
@@ -215,7 +216,7 @@ pub fn new() -> Self {
Self {
outp: Outputter::stdoutput(),
autosuggestion_is_truncated: Default::default(),
scrolled: Default::default(),
scroll_amount: Default::default(),
desired: Default::default(),
actual: Default::default(),
actual_left_prompt: Default::default(),
@@ -229,6 +230,10 @@ 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.
@@ -433,7 +438,7 @@ struct ScrolledCursor {
// Append pager_data (none if empty).
self.desired.append_lines(&page_rendering.screen_data);
self.scrolled = scrolled_cursor.scroll_amount != 0;
self.scroll_amount = scrolled_cursor.scroll_amount;
self.update(
vars,
@@ -473,6 +478,10 @@ 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,
@@ -921,14 +930,15 @@ 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 =
@@ -978,7 +988,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))