Implement jump-to-matching-bracket motion and bind % (percent) in vi mode

Part of #1842
This commit is contained in:
Nikita Bobko 2024-06-28 23:23:36 +02:00 committed by Peter Ammon
parent c966c19c56
commit f8ebe346a9
No known key found for this signature in database
6 changed files with 81 additions and 0 deletions

View file

@ -181,6 +181,7 @@ New or improved bindings
- When the cursor is at the start of a line, escaping from insert mode no longer moves the cursor to the previous line.
- Added bindings for clipboard interaction, like :kbd:`",+,p` and :kbd:`",+,y,y`.
- Deleting in visual mode now moves the cursor back, matching vi (:issue:`10394`).
- Support :kbd:`%` motion.
Completions
^^^^^^^^^^^

View file

@ -258,6 +258,11 @@ The following special input functions are available:
``repeat-jump`` and ``repeat-jump-reverse``
redo the last jump in the same/opposite direction
``jump-to-matching-bracket``
jump to matching bracket if the character under the cursor is bracket;
otherwise, jump to the next occurence of *any right* bracket after the cursor.
The following brackets are considered: ``([{}])``
``kill-bigword``
move the next whitespace-delimited word to the killring

View file

@ -238,6 +238,7 @@ function fish_vi_key_bindings --description 'vi-like key bindings for fish'
bind -s --preset y,i backward-jump-till and repeat-jump-reverse and begin-selection repeat-jump kill-selection yank end-selection
bind -s --preset y,a backward-jump and repeat-jump-reverse and begin-selection repeat-jump kill-selection yank end-selection
bind -s --preset % jump-to-matching-bracket
bind -s --preset f forward-jump
bind -s --preset F backward-jump
bind -s --preset t forward-jump-till
@ -305,6 +306,7 @@ function fish_vi_key_bindings --description 'vi-like key bindings for fish'
bind -s --preset -M visual E 'set fish_cursor_end_mode exclusive' forward-single-char forward-bigword backward-char 'set fish_cursor_end_mode inclusive'
bind -s --preset -M visual o swap-selection-start-stop repaint-mode
bind -s --preset -M visual % jump-to-matching-bracket
bind -s --preset -M visual f forward-jump
bind -s --preset -M visual t forward-jump-till
bind -s --preset -M visual F backward-jump

View file

@ -178,6 +178,7 @@ const INPUT_FUNCTION_METADATA: &[InputFunctionMetadata] = &[
make_md(L!("history-token-search-forward"), ReadlineCmd::HistoryTokenSearchForward),
make_md(L!("insert-line-over"), ReadlineCmd::InsertLineOver),
make_md(L!("insert-line-under"), ReadlineCmd::InsertLineUnder),
make_md(L!("jump-to-matching-bracket"), ReadlineCmd::JumpToMatchingBracket),
make_md(L!("kill-bigword"), ReadlineCmd::KillBigword),
make_md(L!("kill-inner-line"), ReadlineCmd::KillInnerLine),
make_md(L!("kill-line"), ReadlineCmd::KillLine),

View file

@ -109,6 +109,7 @@ pub enum ReadlineCmd {
BackwardJump,
ForwardJumpTill,
BackwardJumpTill,
JumpToMatchingBracket,
FuncAnd,
FuncOr,
ExpandAbbr,

View file

@ -1739,6 +1739,42 @@ impl ReaderData {
}
}
fn jump_to_matching_bracket(
&mut self,
precision: JumpPrecision,
elt: EditableLineTag,
jump_from: usize,
l_bracket: char,
r_bracket: char,
) -> bool {
let el = self.edit_line(elt);
let mut tmp_r_pos: usize = 0;
let mut brackets_stack = Vec::new();
while tmp_r_pos < el.len() {
if el.at(tmp_r_pos) == l_bracket {
brackets_stack.push(tmp_r_pos);
} else if el.at(tmp_r_pos) == r_bracket {
match brackets_stack.pop() {
Some(tmp_l_pos) if jump_from == tmp_l_pos => {
return match precision {
JumpPrecision::Till => self.update_buff_pos(elt, Some(tmp_r_pos - 1)),
JumpPrecision::To => self.update_buff_pos(elt, Some(tmp_r_pos)),
};
}
Some(tmp_l_pos) if jump_from == tmp_r_pos => {
return match precision {
JumpPrecision::Till => self.update_buff_pos(elt, Some(tmp_l_pos + 1)),
JumpPrecision::To => self.update_buff_pos(elt, Some(tmp_l_pos)),
};
}
_ => {}
}
}
tmp_r_pos += 1;
}
return false;
}
fn jump_and_remember_last_jump(
&mut self,
direction: JumpDirection,
@ -3113,6 +3149,41 @@ impl<'a> Reader<'a> {
self.input_data.function_set_status(success);
}
}
rl::JumpToMatchingBracket => {
let (elt, _el) = self.active_edit_line();
let el = self.edit_line(elt);
let l_brackets = ['(', '[', '{'];
let r_brackets = [')', ']', '}'];
let jump_from_pos = el.position();
let precision = JumpPrecision::To;
let success = if l_brackets.contains(&el.at(jump_from_pos))
|| r_brackets.contains(&el.at(jump_from_pos))
{
let l_bracket = match el.at(jump_from_pos) {
'(' | ')' => '(',
'[' | ']' => '[',
'{' | '}' => '{',
_ => unreachable!(),
};
let r_bracket = match l_bracket {
'(' => ')',
'[' => ']',
'{' => '}',
_ => unreachable!(),
};
self.jump_to_matching_bracket(
precision,
elt,
jump_from_pos,
l_bracket,
r_bracket,
)
} else {
// If we stand on non-bracket character, we prefer to jump forward
self.jump(JumpDirection::Forward, precision, elt, r_brackets.to_vec())
};
self.input_data.function_set_status(success);
}
rl::RepeatJump => {
let (elt, _el) = self.active_edit_line();
let mut success = false;