From cf620c829b4b826cafb2ed92349f8324d8b5045f Mon Sep 17 00:00:00 2001 From: SeekingBlues Date: Mon, 30 May 2022 22:12:24 -0500 Subject: [PATCH] Improve newline behavior of `kill-whole-line` Previously, `kill-whole-line` kills the line and its following newline. This is insufficient when we are on the last line, because it would not actually clear the line. The cursor would stay on the line, which is not the correct behavior for bindings like `dd`. Also, `cc` in vi-mode used `kill-whole-line`, which is not correct because it should not remove any newlines. We have to introduce another special input function (`kill-inner-line`) to fix this. --- CHANGELOG.rst | 2 ++ doc_src/cmds/bind.rst | 5 ++++- share/functions/fish_vi_key_bindings.fish | 4 ++-- src/input.cpp | 1 + src/input_common.h | 1 + src/reader.cpp | 27 +++++++++++++++++------ 6 files changed, 30 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index c0f1bd25a..167c91ead 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -93,6 +93,8 @@ Interactive improvements New or improved bindings ^^^^^^^^^^^^^^^^^^^^^^^^ - Keyboard shortcut :kbd:`Alt-S` (previously: toggle ``sudo`` prepended to current commandline contents) now supports ``doas`` on systems without ``sudo`` (:issue:`8942`). +- The ``kill-whole-line`` special input function now kills the newline preceeding the last line. This makes ``dd`` in vi-mode clear the last line properly. +- Introduce the ``kill-inner-line`` special input function, which kills the line without any newlines, allowing ``cc`` in vi-mode to clear the line while preserving newlines. Improved prompts ^^^^^^^^^^^^^^^^ diff --git a/doc_src/cmds/bind.rst b/doc_src/cmds/bind.rst index 37970a07b..f76daad0c 100644 --- a/doc_src/cmds/bind.rst +++ b/doc_src/cmds/bind.rst @@ -230,7 +230,10 @@ The following special input functions are available: move the selected text to the killring ``kill-whole-line`` - move the line to the killring + move the line (including the following newline) to the killring. If the line is the last line, its preceeding newline is also removed + +``kill-inner-line`` + move the line (without the following newline) to the killring ``kill-word`` move the next word to the killring diff --git a/share/functions/fish_vi_key_bindings.fish b/share/functions/fish_vi_key_bindings.fish index 284c0303f..fb6c89ca1 100644 --- a/share/functions/fish_vi_key_bindings.fish +++ b/share/functions/fish_vi_key_bindings.fish @@ -153,8 +153,8 @@ function fish_vi_key_bindings --description 'vi-like key bindings for fish' bind -s --preset 'd,' begin-selection repeat-jump-reverse kill-selection end-selection bind -s --preset -m insert s delete-char repaint-mode - bind -s --preset -m insert S kill-whole-line repaint-mode - bind -s --preset -m insert cc kill-whole-line repaint-mode + bind -s --preset -m insert S kill-inner-line repaint-mode + bind -s --preset -m insert cc kill-inner-line repaint-mode bind -s --preset -m insert C kill-line repaint-mode bind -s --preset -m insert c\$ kill-line repaint-mode bind -s --preset -m insert c\^ backward-kill-line repaint-mode diff --git a/src/input.cpp b/src/input.cpp index dc4825487..e360ffa1e 100644 --- a/src/input.cpp +++ b/src/input.cpp @@ -139,6 +139,7 @@ static constexpr const input_function_metadata_t input_function_metadata[] = { {L"insert-line-over", readline_cmd_t::insert_line_over}, {L"insert-line-under", readline_cmd_t::insert_line_under}, {L"kill-bigword", readline_cmd_t::kill_bigword}, + {L"kill-inner-line", readline_cmd_t::kill_inner_line}, {L"kill-line", readline_cmd_t::kill_line}, {L"kill-selection", readline_cmd_t::kill_selection}, {L"kill-whole-line", readline_cmd_t::kill_whole_line}, diff --git a/src/input_common.h b/src/input_common.h index da03e7ca8..6cec3c7f7 100644 --- a/src/input_common.h +++ b/src/input_common.h @@ -37,6 +37,7 @@ enum class readline_cmd_t { end_of_history, backward_kill_line, kill_whole_line, + kill_inner_line, kill_word, kill_bigword, backward_kill_word, diff --git a/src/reader.cpp b/src/reader.cpp index 9a5bbcbe0..51900950f 100644 --- a/src/reader.cpp +++ b/src/reader.cpp @@ -1479,6 +1479,7 @@ static bool command_ends_paging(readline_cmd_t c, bool focused_on_search_field) case rl::yank_pop: case rl::backward_kill_line: case rl::kill_whole_line: + case rl::kill_inner_line: case rl::kill_word: case rl::kill_bigword: case rl::backward_kill_word: @@ -3295,9 +3296,10 @@ void reader_data_t::handle_readline_command(readline_cmd_t c, readline_loop_stat kill(el, begin - buff, len, KILL_PREPEND, rls.last_cmd != rl::backward_kill_line); break; } - case rl::kill_whole_line: { - // We match the emacs behavior here: "kills the entire line including the following - // newline". + case rl::kill_whole_line: // We match the emacs behavior here: "kills the entire line + // including the following newline". + case rl::kill_inner_line: // Do not kill the following newline + { editable_line_t *el = active_edit_line(); const wchar_t *buff = el->text().c_str(); @@ -3312,16 +3314,27 @@ void reader_data_t::handle_readline_command(readline_cmd_t c, readline_loop_stat // Push end forwards to just past the next newline, or just past the last char. size_t end = el->position(); - while (buff[end] != L'\0') { - end++; - if (buff[end - 1] == L'\n') { + for (;; end++) { + if (buff[end] == L'\0') { + if (c == rl::kill_whole_line && begin > 0) { + // We are on the last line. Delete the newline in the beginning to clear + // this line. + begin--; + } + break; + } + if (buff[end] == L'\n') { + if (c == rl::kill_whole_line) { + end++; + } break; } } + assert(end >= begin); if (end > begin) { - kill(el, begin, end - begin, KILL_APPEND, rls.last_cmd != rl::kill_whole_line); + kill(el, begin, end - begin, KILL_APPEND, rls.last_cmd != c); } break; }