Change vi-mode tilde to toggle character case

This updates the behavior of tilde to match the behavior found in vim.
In vim, tilde toggles the case of the character under the cursor and
advances one character. In visual mode, the case of each selected
character is toggled, the cursor position moves to the beginning of
the selection, and the mode is changed to normal. In fish, tilde
capitalizes the current letter and advances one word. There is no
current tilde command for visual mode in fish.

This patch adds the readline commands `togglecase-letter` and
`togglecase-selection` to match the behavior of vim more closely. The
only difference is that in visual mode, the cursor is not modified.
Modifying the cursor in visual mode would require either moving it in
`togglecase-selection`, which seems outside its scope or adding
something like a `move-to-selection-start` readline command.
This commit is contained in:
Joel Kuhn
2020-04-16 23:24:25 -04:00
committed by Johannes Altmanninger
parent 93b86bbe63
commit a3dfa21737
4 changed files with 72 additions and 1 deletions

View File

@@ -187,7 +187,7 @@ function fish_vi_key_bindings --description 'vi-like key bindings for fish'
bind -s --preset -m insert ci backward-jump-till and repeat-jump-reverse and begin-selection repeat-jump kill-selection end-selection repaint-mode
bind -s --preset -m insert ca backward-jump and repeat-jump-reverse and begin-selection repeat-jump kill-selection end-selection repaint-mode
bind -s --preset '~' capitalize-word
bind -s --preset '~' togglecase-char forward-char
bind -s --preset gu downcase-word
bind -s --preset gU upcase-word
@@ -291,6 +291,7 @@ function fish_vi_key_bindings --description 'vi-like key bindings for fish'
bind -s --preset -M visual -m default X kill-whole-line end-selection repaint-mode
bind -s --preset -M visual -m default y kill-selection yank end-selection repaint-mode
bind -s --preset -M visual -m default '"*y' "commandline -s | xsel -p; commandline -f end-selection repaint-mode"
bind -s --preset -M visual -m default '~' togglecase-selection end-selection repaint-mode
bind -s --preset -M visual -m default \cc end-selection repaint-mode
bind -s --preset -M visual -m default \e end-selection repaint-mode

View File

@@ -123,6 +123,8 @@ static const input_function_metadata_t input_function_metadata[] = {
{readline_cmd_t::upcase_word, L"upcase-word"},
{readline_cmd_t::downcase_word, L"downcase-word"},
{readline_cmd_t::capitalize_word, L"capitalize-word"},
{readline_cmd_t::togglecase_char, L"togglecase-char"},
{readline_cmd_t::togglecase_selection, L"togglecase-selection"},
{readline_cmd_t::execute, L"execute"},
{readline_cmd_t::beginning_of_buffer, L"beginning-of-buffer"},
{readline_cmd_t::end_of_buffer, L"end-of-buffer"},

View File

@@ -48,6 +48,8 @@ enum class readline_cmd_t {
upcase_word,
downcase_word,
capitalize_word,
togglecase_char,
togglecase_selection,
execute,
beginning_of_buffer,
end_of_buffer,

View File

@@ -3219,6 +3219,72 @@ void reader_data_t::handle_readline_command(readline_cmd_t c, readline_loop_stat
}
break;
}
case rl::togglecase_char: {
editable_line_t *el = active_edit_line();
size_t buff_pos = el->position();
// Check that the cursor is on a character
if (buff_pos < el->size()) {
wchar_t chr = el->text().at(buff_pos);
wcstring replacement;
// Toggle the case of the current character
bool make_uppercase = iswlower(chr);
if (make_uppercase) {
chr = towupper(chr);
} else {
chr = tolower(chr);
}
replacement.push_back(chr);
el->replace_substring(buff_pos, (size_t)1, std::move(replacement));
// Restore the buffer position since replace_substring moves
// the buffer position ahead of the replaced text.
update_buff_pos(el, buff_pos);
command_line_changed(el);
super_highlight_me_plenty();
reader_repaint_needed();
}
break;
}
case rl::togglecase_selection: {
editable_line_t *el = active_edit_line();
// Check that we have an active selection and get the bounds.
size_t start, len;
if (reader_get_selection(&start, &len)) {
size_t buff_pos = el->position();
wcstring replacement;
// Loop through the selected characters and toggle their case.
for (size_t pos = start; pos < start + len && pos < el->size(); pos++) {
wchar_t chr = el->text().at(pos);
// Toggle the case of the current character.
bool make_uppercase = iswlower(chr);
if (make_uppercase) {
chr = towupper(chr);
} else {
chr = tolower(chr);
}
replacement.push_back(chr);
}
el->replace_substring(start, len, std::move(replacement));
// Restore the buffer position since replace_substring moves
// the buffer position ahead of the replaced text.
update_buff_pos(el, buff_pos);
command_line_changed(el);
super_highlight_me_plenty();
reader_repaint_needed();
}
break;
}
case rl::upcase_word:
case rl::downcase_word:
case rl::capitalize_word: {