diff --git a/share/functions/fish_vi_key_bindings.fish b/share/functions/fish_vi_key_bindings.fish index ddfaf6e86..f83bfe0a3 100644 --- a/share/functions/fish_vi_key_bindings.fish +++ b/share/functions/fish_vi_key_bindings.fish @@ -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 diff --git a/src/input.cpp b/src/input.cpp index 72f84a509..0c53d77ca 100644 --- a/src/input.cpp +++ b/src/input.cpp @@ -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"}, diff --git a/src/input_common.h b/src/input_common.h index 746b4f6d6..901acf678 100644 --- a/src/input_common.h +++ b/src/input_common.h @@ -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, diff --git a/src/reader.cpp b/src/reader.cpp index f607cbff4..ce2a0bc3a 100644 --- a/src/reader.cpp +++ b/src/reader.cpp @@ -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: {