mirror of
https://github.com/fish-shell/fish-shell
synced 2025-01-25 19:25:06 +00:00
ctrl-l to scroll content instead of erasing screen
On ctrl-l we send `\e[2J` (Erase in Display). Some terminals interpret this to scroll the screen content instead of clearing it. This happens on VTE-based terminals like gnome-terminal for example. The traditional behavior of ctrl-l erasing the screen (but not the rest of the scrollback) is weird because: 1. `ctrl-l` is the easiest and most portable way to push the prompt to the top (and repaint after glitches I guess). But it's also a destructive action, truncating scrollback. I use it for scrolling and am frequently surprised when my scroll back is missing information. 2. the amount of lines erased depends on the window size. It would be more intuitive to erase by prompts, or erase the text in the terminal selection. Let's use scrolling behavior on all terminals. The new command could also be named "push-to-scrollback", for consistency with others. But if we anticipate a want to add other scrollback-related commands, "scrollback-push" is better. This causes tests/checks/tmux-history-search.fish to fail; that test seems pretty broken; M-d (alt-d) is supposed to delete the current search match but there is a rogue "echo" that is supposed to invalidate the search match. I'm not sure how that ever worked. Also, pexepect doesn't seem to support cursor position reporting, so work around that. Ref: https://codeberg.org/dnkl/foot/wiki#how-do-i-make-ctrl-l-scroll-the-content-instead-of-erasing-it as of wiki commit b57489e298f95d037fdf34da00ea60a5e8eafd6d Closes #10934
This commit is contained in:
parent
84f19a931d
commit
83b0294fc9
11 changed files with 54 additions and 5 deletions
|
@ -17,6 +17,8 @@ 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.
|
||||
- The OSC 133 prompt marking feature has learned about kitty's ``click_events=1`` flag, which allows moving fish's cursor by clicking.
|
||||
- :kbd:`ctrl-l` no longer clears the screen but only pushes to the terminal's scrollback all text above the prompt (via a new special input function ``scrollback-push``).
|
||||
You can restore previous behavior with `bind ctrl-l clear-screen`.
|
||||
|
||||
Completions
|
||||
^^^^^^^^^^^
|
||||
|
|
|
@ -171,7 +171,10 @@ The following special input functions are available:
|
|||
make the current word begin with a capital letter
|
||||
|
||||
``clear-screen``
|
||||
clears the screen and redraws the prompt. if the terminal doesn't support clearing the screen it is the same as ``repaint``.
|
||||
clears the screen and redraws the prompt.
|
||||
|
||||
``scrollback-push``
|
||||
pushes earlier output to the terminal scrollback, positioning the prompt at the top.
|
||||
|
||||
``complete``
|
||||
guess the remainder of the current token
|
||||
|
|
|
@ -66,7 +66,7 @@ function __fish_shared_key_bindings -d "Bindings shared between emacs and vi mod
|
|||
bind --preset $argv alt-l __fish_list_current_token
|
||||
bind --preset $argv alt-o __fish_preview_current_file
|
||||
bind --preset $argv alt-w __fish_whatis_current_token
|
||||
bind --preset $argv ctrl-l clear-screen
|
||||
bind --preset $argv ctrl-l scrollback-push repaint
|
||||
bind --preset $argv ctrl-c cancel-commandline
|
||||
bind --preset $argv ctrl-u backward-kill-line
|
||||
bind --preset $argv ctrl-w backward-kill-path-component
|
||||
|
|
|
@ -66,8 +66,10 @@ pub struct Term {
|
|||
pub cursor_down: Option<CString>,
|
||||
pub cursor_left: Option<CString>,
|
||||
pub cursor_right: Option<CString>,
|
||||
pub parm_cursor_up: Option<CString>,
|
||||
pub parm_left_cursor: Option<CString>,
|
||||
pub parm_right_cursor: Option<CString>,
|
||||
pub parm_index: Option<CString>,
|
||||
pub clr_eol: Option<CString>,
|
||||
pub clr_eos: Option<CString>,
|
||||
|
||||
|
@ -215,8 +217,10 @@ impl Term {
|
|||
cursor_down: get_str_cap(&db, "do"),
|
||||
cursor_left: get_str_cap(&db, "le"),
|
||||
cursor_right: get_str_cap(&db, "nd"),
|
||||
parm_cursor_up: get_str_cap(&db, "UP"),
|
||||
parm_left_cursor: get_str_cap(&db, "LE"),
|
||||
parm_right_cursor: get_str_cap(&db, "RI"),
|
||||
parm_index: get_str_cap(&db, "SF"),
|
||||
clr_eol: get_str_cap(&db, "ce"),
|
||||
clr_eos: get_str_cap(&db, "cd"),
|
||||
|
||||
|
@ -425,8 +429,10 @@ pub fn setup_fallback_term() -> Arc<Term> {
|
|||
cursor_down: Some(CString::new("\n").unwrap()),
|
||||
cursor_left: Some(CString::new("\x08").unwrap()),
|
||||
cursor_right: Some(CString::new("\x1b[C").unwrap()),
|
||||
parm_cursor_up: Some(CString::new("\x1b[%p1%dA").unwrap()),
|
||||
parm_left_cursor: Some(CString::new("\x1b[%p1%dD").unwrap()),
|
||||
parm_right_cursor: Some(CString::new("\x1b[%p1%dC").unwrap()),
|
||||
parm_index: Some(CString::new("\x1b[%p1%dS").unwrap()),
|
||||
clr_eol: Some(CString::new("\x1b[K").unwrap()),
|
||||
clr_eos: Some(CString::new("\x1b[J").unwrap()),
|
||||
max_colors: Some(256),
|
||||
|
|
|
@ -198,6 +198,7 @@ const INPUT_FUNCTION_METADATA: &[InputFunctionMetadata] = &[
|
|||
make_md(L!("repaint-mode"), ReadlineCmd::RepaintMode),
|
||||
make_md(L!("repeat-jump"), ReadlineCmd::RepeatJump),
|
||||
make_md(L!("repeat-jump-reverse"), ReadlineCmd::ReverseRepeatJump),
|
||||
make_md(L!("scrollback-push"), ReadlineCmd::ScrollbackPush),
|
||||
make_md(L!("self-insert"), ReadlineCmd::SelfInsert),
|
||||
make_md(L!("self-insert-notfirst"), ReadlineCmd::SelfInsertNotFirst),
|
||||
make_md(L!("suppress-autosuggestion"), ReadlineCmd::SuppressAutosuggestion),
|
||||
|
|
|
@ -131,6 +131,7 @@ pub enum ReadlineCmd {
|
|||
EndUndoGroup,
|
||||
RepeatJump,
|
||||
ClearScreenAndRepaint,
|
||||
ScrollbackPush,
|
||||
// NOTE: This one has to be last.
|
||||
ReverseRepeatJump,
|
||||
}
|
||||
|
@ -191,6 +192,8 @@ pub enum ImplicitEvent {
|
|||
DisableMouseTracking,
|
||||
/// Handle mouse left click.
|
||||
MouseLeftClickContinuation(ViewportPosition, ViewportPosition),
|
||||
/// Push prompt to top.
|
||||
ScrollbackPushContinuation(usize),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
@ -590,6 +593,7 @@ impl InputData {
|
|||
|
||||
pub enum WaitingForCursorPosition {
|
||||
MouseLeft(ViewportPosition),
|
||||
ScrollbackPush,
|
||||
}
|
||||
|
||||
/// A trait which knows how to produce a stream of input events.
|
||||
|
@ -1025,6 +1029,9 @@ pub trait InputEventQueuer {
|
|||
*click_position,
|
||||
)
|
||||
}
|
||||
WaitingForCursorPosition::ScrollbackPush => {
|
||||
ImplicitEvent::ScrollbackPushContinuation(y)
|
||||
}
|
||||
};
|
||||
self.push_front(CharEvent::Implicit(continuation));
|
||||
return None;
|
||||
|
|
|
@ -2275,6 +2275,10 @@ impl<'a> Reader<'a> {
|
|||
self.mouse_left_click(cursor, click_position);
|
||||
self.stop_waiting_for_cursor_position();
|
||||
}
|
||||
ImplicitEvent::ScrollbackPushContinuation(cursor_y) => {
|
||||
self.screen.push_to_scrollback(cursor_y);
|
||||
self.stop_waiting_for_cursor_position();
|
||||
}
|
||||
},
|
||||
}
|
||||
ControlFlow::Continue(())
|
||||
|
@ -3499,6 +3503,9 @@ impl<'a> Reader<'a> {
|
|||
self.force_exec_prompt_and_repaint = false;
|
||||
self.parser.libdata_mut().is_repaint = false;
|
||||
}
|
||||
rl::ScrollbackPush => {
|
||||
self.request_cursor_position(WaitingForCursorPosition::ScrollbackPush);
|
||||
}
|
||||
rl::SelfInsert | rl::SelfInsertNotFirst | rl::FuncAnd | rl::FuncOr => {
|
||||
// This can be reached via `commandline -f and` etc
|
||||
// panic!("should have been handled by inputter_t::readch");
|
||||
|
|
|
@ -488,6 +488,28 @@ impl Screen {
|
|||
self.r#move(0, self.actual.line_count());
|
||||
}
|
||||
|
||||
pub fn push_to_scrollback(&mut self, cursor_y: usize) {
|
||||
let mut prompt_y = self.command_line_y_given_cursor_y(cursor_y);
|
||||
prompt_y -= calc_prompt_lines(&self.actual_left_prompt) - 1;
|
||||
if prompt_y == 0 {
|
||||
return;
|
||||
}
|
||||
let zelf = self.scoped_buffer();
|
||||
let Some(term) = term() else {
|
||||
return;
|
||||
};
|
||||
let mut out = zelf.outp.borrow_mut();
|
||||
let prompt_y = i32::try_from(prompt_y).unwrap();
|
||||
// Scroll down.
|
||||
if let Some(scroll) = term.parm_index.as_ref() {
|
||||
out.tputs_if_some(&tparm1(scroll, prompt_y));
|
||||
}
|
||||
// Reposition cursor.
|
||||
if let Some(up) = term.parm_cursor_up.as_ref() {
|
||||
out.tputs_if_some(&tparm1(up, prompt_y));
|
||||
}
|
||||
}
|
||||
|
||||
fn command_line_y_given_cursor_y(&mut self, viewport_cursor_y: usize) -> usize {
|
||||
let prompt_y = viewport_cursor_y.checked_sub(self.actual.cursor.y);
|
||||
prompt_y.unwrap_or_else(|| {
|
||||
|
|
|
@ -43,7 +43,7 @@ isolated-tmux capture-pane -p | sed -n '1p;$p'
|
|||
# Also ensure that the pager is actually fully disclosed.
|
||||
# CHECK: rows 1 to {{\d+}} of {{\d+}}
|
||||
|
||||
# Canceling the pager removes the inserted completion, no mater what happens in the search field.
|
||||
# Canceling the pager removes the inserted completion, no matter what happens in the search field.
|
||||
# The common prefix remains because it is inserted before the pager is shown.
|
||||
isolated-tmux send-keys C-c
|
||||
tmux-sleep
|
||||
|
|
|
@ -47,8 +47,7 @@ isolated-tmux capture-pane -p | grep 'prompt 2>'
|
|||
isolated-tmux send-keys C-c
|
||||
|
||||
isolated-tmux send-keys 'echo 1' Enter 'echo 2' Enter 'echo 3' Enter
|
||||
isolated-tmux send-keys C-l echo Up
|
||||
isolated-tmux send-keys echo M-d
|
||||
isolated-tmux send-keys C-l echo Up M-d
|
||||
tmux-sleep
|
||||
isolated-tmux capture-pane -p
|
||||
#CHECK: prompt 5> echo 2
|
||||
|
|
|
@ -22,6 +22,8 @@ send, sendline, sleep, expect_prompt, expect_re, expect_str = (
|
|||
)
|
||||
expect_prompt()
|
||||
|
||||
sendline("bind ctrl-l repaint")
|
||||
expect_prompt()
|
||||
# Clear twice (regression test for #7280).
|
||||
send("\f")
|
||||
expect_prompt(increment=False)
|
||||
|
|
Loading…
Reference in a new issue