Skip to content

Commit

Permalink
Add till/repeat/reverse jump bindings
Browse files Browse the repository at this point in the history
- Add support for:
  - Jumping to the character before a target.
  - Repeating the previous jump (same direction, same precision).
  - Repeating the previous jump in the reverse order.
- Enhance vi bindings.
  • Loading branch information
cttttt committed Aug 12, 2018
1 parent fa66ac8 commit 05e1366
Show file tree
Hide file tree
Showing 6 changed files with 191 additions and 24 deletions.
10 changes: 6 additions & 4 deletions share/functions/fish_vi_key_bindings.fish
Original file line number Diff line number Diff line change
Expand Up @@ -207,8 +207,10 @@ function fish_vi_key_bindings --description 'vi-like key bindings for fish'

bind f forward-jump
bind F backward-jump
bind t forward-jump and backward-char
bind T backward-jump and forward-char
bind t forward-jump-till
bind T backward-jump-till
bind ';' repeat-jump
bind , repeat-jump-reverse

# in emacs yank means paste
bind p yank
Expand Down Expand Up @@ -245,9 +247,9 @@ function fish_vi_key_bindings --description 'vi-like key bindings for fish'
bind $argv visual o swap-selection-start-stop force-repaint

bind $argv visual f forward-jump
bind $argv visual t forward-jump backward-char
bind $argv visual t forward-jump-till
bind $argv visual F backward-jump
bind $argv visual T backward-jump forward-char
bind $argv visual T backward-jump-till

for key in $eol_keys
bind $argv visual $key end-of-line
Expand Down
14 changes: 13 additions & 1 deletion src/input.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,10 @@ static const input_function_metadata_t input_function_metadata[] = {
{R_KILL_SELECTION, L"kill-selection"},
{R_FORWARD_JUMP, L"forward-jump"},
{R_BACKWARD_JUMP, L"backward-jump"},
{R_FORWARD_JUMP_TILL, L"forward-jump-till"},
{R_BACKWARD_JUMP_TILL, L"backward-jump-till"},
{R_REPEAT_JUMP, L"repeat-jump"},
{R_REVERSE_REPEAT_JUMP, L"repeat-jump-reverse"},
{R_AND, L"and"},
{R_CANCEL, L"cancel"}};

Expand Down Expand Up @@ -176,7 +180,15 @@ void input_set_bind_mode(const wcstring &bm) {

/// Returns the arity of a given input function.
static int input_function_arity(int function) {
return (function == R_FORWARD_JUMP || function == R_BACKWARD_JUMP) ? 1 : 0;
switch (function) {
case R_FORWARD_JUMP:
case R_BACKWARD_JUMP:
case R_FORWARD_JUMP_TILL:
case R_BACKWARD_JUMP_TILL:
return 1;
default:
return 0;
}
}

/// Sets the return status of the most recently executed input function.
Expand Down
6 changes: 5 additions & 1 deletion src/input_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,15 +67,19 @@ enum {
R_KILL_SELECTION,
R_FORWARD_JUMP,
R_BACKWARD_JUMP,
R_FORWARD_JUMP_TILL,
R_BACKWARD_JUMP_TILL,
R_AND,
R_CANCEL,
R_REPEAT_JUMP,
R_REVERSE_REPEAT_JUMP,

R_TIMEOUT, // we didn't get interactive input within wait_on_escape_ms

// The range of key codes for inputrc-style keyboard functions that are passed on to the caller
// of input_read().
R_BEGIN_INPUT_FUNCTIONS = R_BEGINNING_OF_LINE,
R_END_INPUT_FUNCTIONS = R_CANCEL + 1
R_END_INPUT_FUNCTIONS = R_REVERSE_REPEAT_JUMP + 1
};

/// Init the library.
Expand Down
133 changes: 115 additions & 18 deletions src/reader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,9 @@

enum class history_search_direction_t { forward, backward };

enum class jump_direction_t { forward, backward };
enum class jump_precision_t { till, to };

/// Any time the contents of a buffer changes, we update the generation count. This allows for our
/// background threads to notice it and skip doing work that they would otherwise have to do.
static std::atomic<unsigned int> s_generation_count;
Expand Down Expand Up @@ -363,6 +366,10 @@ class reader_data_t {
bool screen_reset_needed{false};
/// Whether the reader should exit on ^C.
bool exit_on_interrupt{false};
/// The target character of the last jump command.
wchar_t last_jump_target{0};
jump_direction_t last_jump_direction{jump_direction_t::forward};
jump_precision_t last_jump_precision{jump_precision_t::to};

bool is_navigating_pager_contents() const { return this->pager.is_navigating_contents(); }

Expand Down Expand Up @@ -422,6 +429,7 @@ static volatile sig_atomic_t interrupted = 0;
// Prototypes for a bunch of functions defined later on.
static bool is_backslashed(const wcstring &str, size_t pos);
static wchar_t unescaped_quote(const wcstring &str, size_t pos);
bool jump(jump_direction_t dir, jump_precision_t precision, editable_line_t *el, wchar_t target);

/// Mode on startup, which we restore on exit.
static struct termios terminal_mode_on_startup;
Expand Down Expand Up @@ -3179,33 +3187,80 @@ const wchar_t *reader_readline(int nchars) {
case R_FORWARD_JUMP: {
editable_line_t *el = data->active_edit_line();
wchar_t target = input_function_pop_arg();
bool status = false;
bool success = jump(jump_direction_t::forward, jump_precision_t::to, el, target);

for (size_t i = el->position + 1; i < el->size(); i++) {
if (el->at(i) == target) {
update_buff_pos(el, i);
status = true;
break;
}
}
input_function_set_status(status);
input_function_set_status(success);
reader_repaint_needed();
break;
}
case R_BACKWARD_JUMP: {
editable_line_t *el = data->active_edit_line();
wchar_t target = input_function_pop_arg();
bool status = false;
bool success = jump(jump_direction_t::backward, jump_precision_t::to, el, target);

size_t tmp_pos = el->position;
while (tmp_pos--) {
if (el->at(tmp_pos) == target) {
update_buff_pos(el, tmp_pos);
status = true;
break;
}
input_function_set_status(success);
reader_repaint_needed();
break;
}
case R_FORWARD_JUMP_TILL: {
editable_line_t *el = data->active_edit_line();
wchar_t target = input_function_pop_arg();
bool success = jump(jump_direction_t::forward, jump_precision_t::till, el, target);

input_function_set_status(success);
reader_repaint_needed();
break;
}
case R_BACKWARD_JUMP_TILL: {
editable_line_t *el = data->active_edit_line();
wchar_t target = input_function_pop_arg();
bool success = jump(jump_direction_t::backward, jump_precision_t::till, el, target);

input_function_set_status(success);
reader_repaint_needed();
break;
}
case R_REPEAT_JUMP: {
editable_line_t *el = data->active_edit_line();
bool success = false;

if (data->last_jump_target) {
success = jump(
data->last_jump_direction,
data->last_jump_precision,
el,
data->last_jump_target
);
}
input_function_set_status(status);

input_function_set_status(success);
reader_repaint_needed();
break;
}
case R_REVERSE_REPEAT_JUMP: {
editable_line_t *el = data->active_edit_line();
bool success = false;
jump_direction_t original_dir, dir;
original_dir = dir = data->last_jump_direction;

if (data->last_jump_direction == jump_direction_t::forward) {
dir = jump_direction_t::backward;
} else {
dir = jump_direction_t::forward;
}

if (data->last_jump_target) {
success = jump(
dir,
data->last_jump_precision,
el,
data->last_jump_target
);
}

data->last_jump_direction = original_dir;

input_function_set_status(success);
reader_repaint_needed();
break;
}
Expand Down Expand Up @@ -3263,6 +3318,48 @@ const wchar_t *reader_readline(int nchars) {
return finished ? data->command_line.text.c_str() : NULL;
}

bool jump(jump_direction_t dir, jump_precision_t precision, editable_line_t *el, wchar_t target) {
reader_data_t *data = current_data_or_null();
bool success = false;

data->last_jump_target = target;
data->last_jump_direction = dir;
data->last_jump_precision = precision;

switch (dir) {
case jump_direction_t::backward: {
size_t tmp_pos = el->position;

while (tmp_pos--) {
if (el->at(tmp_pos) == target) {
if (precision == jump_precision_t::till) {
tmp_pos = std::min(el->size()-1, tmp_pos+1);
}
update_buff_pos(el, tmp_pos);
success = true;
break;
}
}
break;
}
case jump_direction_t::forward: {
for (size_t tmp_pos=el->position+1; tmp_pos < el->size(); tmp_pos++) {
if (el->at(tmp_pos) == target) {
if (precision == jump_precision_t::till && tmp_pos) {
tmp_pos--;
}
update_buff_pos(el, tmp_pos);
success = true;
break;
}
}
break;
}
}

return success;
}

bool reader_is_in_search_mode() {
reader_data_t *data = current_data_or_null();
return data && data->history_search.active();
Expand Down
48 changes: 48 additions & 0 deletions tests/bind.expect
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,54 @@ expect_prompt -re {\r\nMORE\r\n} {
puts stderr "vi mode delete char, default timeout: long delay"
}

# Test jumping forward til before a character with t
send "echo MORE-TEXT-IS-NICE"
send "\033"
# Delay needed to allow fish to transition to vi "normal" mode.
sleep 0.250
send "0tTD\r"
expect_prompt -re {\r\nMORE\r\n} {
puts "vi mode forward-jump-till character, default timeout: long delay"
} unmatched {
puts stderr "vi mode forward-jump-till character, default timeout: long delay"
}

# Test jumping backward til before a character with T
send "echo MORE-TEXT-IS-NICE"
send "\033"
# Delay needed to allow fish to transition to vi "normal" mode.
sleep 0.250
send "TSD\r"
expect_prompt -re {\r\nMORE-TEXT-IS\r\n} {
puts "vi mode backward-jump-till character, default timeout: long delay"
} unmatched {
puts stderr "vi mode backward-jump-till character, default timeout: long delay"
}

# Test jumping backward with F and repeating
send "echo MORE-TEXT-IS-NICE"
send "\033"
# Delay needed to allow fish to transition to vi "normal" mode.
sleep 0.250
send "F-;D\r"
expect_prompt -re {\r\nMORE-TEXT\r\n} {
puts "vi mode backward-jump-to character and repeat, default timeout: long delay"
} unmatched {
puts stderr "vi mode backward-jump-to character and repeat, default timeout: long delay"
}

# Test jumping backward with F w/reverse jump
send "echo MORE-TEXT-IS-NICE"
send "\033"
# Delay needed to allow fish to transition to vi "normal" mode.
sleep 0.250
send "F-F-,D\r"
expect_prompt -re {\r\nMORE-TEXT-IS\r\n} {
puts "vi mode backward-jump-to character, and reverse, default timeout: long delay"
} unmatched {
puts stderr "vi mode backward-jump-to character, and reverse, default timeout: long delay"
}

# Verify that changing the escape timeout has an effect.
send "set -g fish_escape_delay_ms 200\r"
expect_prompt
Expand Down
4 changes: 4 additions & 0 deletions tests/bind.expect.out
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ vi-mode default timeout set correctly
vi replace line, default timeout: long delay
vi mode replace char, default timeout: long delay
vi mode delete char, default timeout: long delay
vi mode forward-jump-till character, default timeout: long delay
vi mode backward-jump-till character, default timeout: long delay
vi mode backward-jump-to character and repeat, default timeout: long delay
vi mode backward-jump-to character, and reverse, default timeout: long delay
vi replace line, 100ms timeout: long delay
vi replace line, 100ms timeout: short delay
t-binding success
Expand Down

0 comments on commit 05e1366

Please sign in to comment.