Implement jump-till-matching-bracket input function

Part of #1842

It's like jump-to-matching-bracket, but jumps right before the bracket

I will use it to mimic vi 'ab' and 'ib' text objects in the next commit

Given complicated semantics of jump-till-matching-bracket, an alternative name
could be 'jump-inside-matching-brackets'. But that would make names non-symmetrical.
I'm not sure what is worse.
This commit is contained in:
Nikita Bobko 2024-06-29 02:20:15 +02:00 committed by Peter Ammon
parent f8ebe346a9
commit 67e190876a
No known key found for this signature in database
4 changed files with 57 additions and 27 deletions

View File

@ -263,6 +263,13 @@ The following special input functions are available:
otherwise, jump to the next occurence of *any right* bracket after the cursor.
The following brackets are considered: ``([{}])``
``jump-till-matching-bracket``
the same as ``jump-to-matching-bracket`` but offset cursor to the right for left bracket, and offset cursor to the left for right bracket.
The offset is applied for both the position we jump from and position we jump to.
In other words, the cursor will continuously jump inside the brackets but won't reach them by 1 character.
The input function is useful to emulate ``ib`` vi text object.
The following brackets are considered: ``([{}])``
``kill-bigword``
move the next whitespace-delimited word to the killring

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-till-matching-bracket"), ReadlineCmd::JumpTillMatchingBracket),
make_md(L!("jump-to-matching-bracket"), ReadlineCmd::JumpToMatchingBracket),
make_md(L!("kill-bigword"), ReadlineCmd::KillBigword),
make_md(L!("kill-inner-line"), ReadlineCmd::KillInnerLine),

View File

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

View File

@ -3149,38 +3149,59 @@ impl<'a> Reader<'a> {
self.input_data.function_set_status(success);
}
}
rl::JumpToMatchingBracket => {
rl::JumpToMatchingBracket | rl::JumpTillMatchingBracket => {
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 {
let cursor = el.position();
let precision = match c {
rl::JumpToMatchingBracket => JumpPrecision::To,
rl::JumpTillMatchingBracket => JumpPrecision::Till,
_ => unreachable!(),
};
let jump_from_pos = match c {
_ if l_brackets.contains(&el.at(cursor))
|| r_brackets.contains(&el.at(cursor)) =>
{
Some(cursor)
}
rl::JumpTillMatchingBracket
if cursor > 0 && l_brackets.contains(&el.at(cursor - 1)) =>
{
Some(cursor - 1)
}
rl::JumpTillMatchingBracket
if cursor < el.len() && r_brackets.contains(&el.at(cursor + 1)) =>
{
Some(cursor + 1)
}
_ => None,
};
let success = match jump_from_pos {
Some(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,
)
}
// If we stand on non-bracket character, we prefer to jump forward
self.jump(JumpDirection::Forward, precision, elt, r_brackets.to_vec())
None => self.jump(JumpDirection::Forward, precision, elt, r_brackets.to_vec()),
};
self.input_data.function_set_status(success);
}