From 18c65df3c727009e935c9bd2e6da23dc5b73d13c Mon Sep 17 00:00:00 2001 From: Fabian Boehm Date: Sun, 8 Oct 2023 11:41:30 +0200 Subject: [PATCH] Add a clear-screen bind function to clear the screen (#10044) This can be bound like `bind \cl clear-screen`, and is, by default In contrast to the current way it doesn't need the external `clear` command that was always awkward. Also it will clear the screen and first draw the old prompt to remove flicker. Then it will immediately trigger a repaint, so the prompt will be overwritten. (cherry picked from commit c4ca1a68d361c2f0e636c2f5aba53fc0c35a52f1) --- doc_src/cmds/bind.rst | 3 +++ .../functions/__fish_shared_key_bindings.fish | 4 +--- src/input.cpp | 1 + src/input_common.h | 2 ++ src/reader.cpp | 21 +++++++++++++++++++ src/screen.cpp | 7 +++++++ src/screen.h | 1 + 7 files changed, 36 insertions(+), 3 deletions(-) diff --git a/doc_src/cmds/bind.rst b/doc_src/cmds/bind.rst index 3a481f40c..451fa3d00 100644 --- a/doc_src/cmds/bind.rst +++ b/doc_src/cmds/bind.rst @@ -143,6 +143,9 @@ The following special input functions are available: ``capitalize-word`` 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``. + ``complete`` guess the remainder of the current token diff --git a/share/functions/__fish_shared_key_bindings.fish b/share/functions/__fish_shared_key_bindings.fish index eaf4f132f..ccaacb3f8 100644 --- a/share/functions/__fish_shared_key_bindings.fish +++ b/share/functions/__fish_shared_key_bindings.fish @@ -86,9 +86,7 @@ function __fish_shared_key_bindings -d "Bindings shared between emacs and vi mod bind --preset $argv \el __fish_list_current_token bind --preset $argv \eo __fish_preview_current_file bind --preset $argv \ew __fish_whatis_current_token - # ncurses > 6.0 sends a "delete scrollback" sequence along with clear. - # This string replace removes it. - bind --preset $argv \cl 'echo -n (clear | string replace \e\[3J ""); commandline -f repaint' + bind --preset $argv \cl clear-screen bind --preset $argv \cc cancel-commandline bind --preset $argv \cu backward-kill-line bind --preset $argv \cw backward-kill-path-component diff --git a/src/input.cpp b/src/input.cpp index 562cf7f33..25c3f20d1 100644 --- a/src/input.cpp +++ b/src/input.cpp @@ -106,6 +106,7 @@ static constexpr const input_function_metadata_t input_function_metadata[] = { {L"cancel", readline_cmd_t::cancel}, {L"cancel-commandline", readline_cmd_t::cancel_commandline}, {L"capitalize-word", readline_cmd_t::capitalize_word}, + {L"clear-screen", readline_cmd_t::clear_screen_and_repaint}, {L"complete", readline_cmd_t::complete}, {L"complete-and-search", readline_cmd_t::complete_and_search}, {L"delete-char", readline_cmd_t::delete_char}, diff --git a/src/input_common.h b/src/input_common.h index a53c46b5d..bef8b1694 100644 --- a/src/input_common.h +++ b/src/input_common.h @@ -91,6 +91,8 @@ enum class readline_cmd_t { end_undo_group, repeat_jump, disable_mouse_tracking, + // ncurses uses the obvious name + clear_screen_and_repaint, // NOTE: This one has to be last. reverse_repeat_jump }; diff --git a/src/reader.cpp b/src/reader.cpp index 351c12e4d..036d2096b 100644 --- a/src/reader.cpp +++ b/src/reader.cpp @@ -4243,6 +4243,27 @@ void reader_data_t::handle_readline_command(readline_cmd_t c, readline_loop_stat outp.writestr(L"\x1B[?1000l"); break; } + case rl::clear_screen_and_repaint: { + parser().libdata().is_repaint = true; + auto clear = screen_clear(); + if (!clear.empty()) { + // Clear the screen if we can. + // This is subtle: We first clear, draw the old prompt, + // and *then* reexecute the prompt and overdraw it. + // This removes the flicker, + // while keeping the prompt up-to-date. + outputter_t &outp = outputter_t::stdoutput(); + outp.writestr(clear.c_str()); + screen.reset_line(true /* redraw prompt */); + this->layout_and_repaint(L"readline"); + } + exec_prompt(); + screen.reset_line(true /* redraw prompt */); + this->layout_and_repaint(L"readline"); + force_exec_prompt_and_repaint = false; + parser().libdata().is_repaint = false; + break; + } // Some commands should have been handled internally by inputter_t::readch(). case rl::self_insert: case rl::self_insert_notfirst: diff --git a/src/screen.cpp b/src/screen.cpp index 7cb6cf52f..28733893f 100644 --- a/src/screen.cpp +++ b/src/screen.cpp @@ -266,6 +266,13 @@ maybe_t escape_code_length(const wchar_t *code) { return found ? maybe_t{esc_seq_len} : none(); } +wcstring screen_clear() { + if (clear_screen) { + return str2wcstring(clear_screen); + } + return wcstring{}; +} + size_t layout_cache_t::escape_code_length(const wchar_t *code) { assert(code != nullptr); if (*code != L'\x1B') return 0; diff --git a/src/screen.h b/src/screen.h index 26bbb452b..64f3dc7a5 100644 --- a/src/screen.h +++ b/src/screen.h @@ -331,5 +331,6 @@ class layout_cache_t : noncopyable_t { maybe_t escape_code_length(const wchar_t *code); +wcstring screen_clear(); void screen_set_midnight_commander_hack(); #endif