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.
This commit is contained in:
SeekingBlues 2022-05-30 22:12:24 -05:00 committed by ridiculousfish
parent 025acfe45a
commit cf620c829b
6 changed files with 30 additions and 10 deletions

View file

@ -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
^^^^^^^^^^^^^^^^

View file

@ -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

View file

@ -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

View file

@ -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},

View file

@ -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,

View file

@ -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;
}