fn forward_match(chars: &[char], from: usize, close: char) -> Option<usize> {
let open = chars[from];
let mut depth: usize = 1;
for (index, &character) in chars.iter().enumerate().skip(from + 1) {
if character == open {
depth += 1;
} else if character == close {
depth -= 1;
if depth == 0 {
return Some(index);
}
}
}
None
}
fn backward_match(chars: &[char], from: usize, open: char) -> Option<usize> {
let close = chars[from];
let mut depth: usize = 1;
for index in (0..from).rev() {
match chars[index] {
c if c == close => depth += 1,
c if c == open => {
depth -= 1;
if depth == 0 {
return Some(index);
}
}
_ => {}
}
}
None
}
#[must_use]
pub fn find_matching_bracket(text: &str, cursor: usize) -> Option<(usize, usize)> {
let chars: Vec<char> = text.chars().collect();
let len = chars.len();
if cursor < len
&& let Some(result) = bracket_at(&chars, cursor)
{
return Some(result);
}
if cursor > 0
&& let Some(result) = bracket_at(&chars, cursor - 1)
{
return Some(result);
}
None
}
fn bracket_at(chars: &[char], index: usize) -> Option<(usize, usize)> {
match chars[index] {
'(' | '[' | '{' => forward_match(chars, index, closing(chars[index])).map(|m| (index, m)),
')' | ']' | '}' => backward_match(chars, index, opening(chars[index])).map(|m| (m, index)),
_ => None,
}
}
const fn closing(open: char) -> char {
match open {
'(' => ')',
'[' => ']',
_ => '}',
}
}
const fn opening(close: char) -> char {
match close {
')' => '(',
']' => '[',
_ => '{',
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn simple_pair() {
assert_eq!(
find_matching_bracket("(hello)", 1), Some((0, 6))
);
}
#[test]
fn cursor_before_close() {
assert_eq!(
find_matching_bracket("(hello)", 6), Some((0, 6))
);
}
#[test]
fn nested_brackets() {
assert_eq!(
find_matching_bracket("a(b[c]d)e", 4), Some((3, 5))
);
}
#[test]
fn unmatched() {
assert_eq!(find_matching_bracket("(hello", 1), None);
}
#[test]
fn mixed_types() {
assert_eq!(
find_matching_bracket("([)]", 3), Some((1, 3))
);
}
#[test]
fn no_bracket() {
assert_eq!(find_matching_bracket("hello", 2), None);
}
#[test]
fn cursor_at_open() {
assert_eq!(
find_matching_bracket("(x)", 0), Some((0, 2))
);
}
#[test]
fn cursor_at_close() {
assert_eq!(
find_matching_bracket("(x)", 2), Some((0, 2))
);
}
#[test]
fn empty_text() {
assert_eq!(find_matching_bracket("", 0), None);
}
}