Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'helix-core/src/match_brackets.rs')
| -rw-r--r-- | helix-core/src/match_brackets.rs | 98 |
1 files changed, 72 insertions, 26 deletions
diff --git a/helix-core/src/match_brackets.rs b/helix-core/src/match_brackets.rs index b8bcc28c..95d6a3dc 100644 --- a/helix-core/src/match_brackets.rs +++ b/helix-core/src/match_brackets.rs @@ -9,16 +9,32 @@ use crate::Syntax; const MAX_PLAINTEXT_SCAN: usize = 10000; const MATCH_LIMIT: usize = 16; -// Limit matching pairs to only ( ) { } [ ] < > ' ' " " -const PAIRS: &[(char, char)] = &[ +pub const BRACKETS: [(char, char); 7] = [ ('(', ')'), ('{', '}'), ('[', ']'), ('<', '>'), - ('\'', '\''), - ('\"', '\"'), + ('«', '»'), + ('「', '」'), + ('(', ')'), ]; +// The difference between BRACKETS and PAIRS is that we can find matching +// BRACKETS in a plain text file, but we can't do the same for PAIRs. +// PAIRS also contains all BRACKETS. +pub const PAIRS: [(char, char); BRACKETS.len() + 3] = { + let mut pairs = [(' ', ' '); BRACKETS.len() + 3]; + let mut idx = 0; + while idx < BRACKETS.len() { + pairs[idx] = BRACKETS[idx]; + idx += 1; + } + pairs[idx] = ('"', '"'); + pairs[idx + 1] = ('\'', '\''); + pairs[idx + 2] = ('`', '`'); + pairs +}; + /// Returns the position of the matching bracket under cursor. /// /// If the cursor is on the opening bracket, the position of @@ -30,7 +46,7 @@ const PAIRS: &[(char, char)] = &[ /// If no matching bracket is found, `None` is returned. #[must_use] pub fn find_matching_bracket(syntax: &Syntax, doc: RopeSlice, pos: usize) -> Option<usize> { - if pos >= doc.len_chars() || !is_valid_bracket(doc.char(pos)) { + if pos >= doc.len_chars() || !is_valid_pair(doc.char(pos)) { return None; } find_pair(syntax, doc, pos, false) @@ -67,7 +83,7 @@ fn find_pair( let (start_byte, end_byte) = surrounding_bytes(doc, &node)?; let (start_char, end_char) = (doc.byte_to_char(start_byte), doc.byte_to_char(end_byte)); - if is_valid_pair(doc, start_char, end_char) { + if is_valid_pair_on_pos(doc, start_char, end_char) { if end_byte == pos { return Some(start_char); } @@ -140,14 +156,22 @@ fn find_pair( /// If no matching bracket is found, `None` is returned. #[must_use] pub fn find_matching_bracket_plaintext(doc: RopeSlice, cursor_pos: usize) -> Option<usize> { - // Don't do anything when the cursor is not on top of a bracket. let bracket = doc.get_char(cursor_pos)?; + let matching_bracket = { + let pair = get_pair(bracket); + if pair.0 == bracket { + pair.1 + } else { + pair.0 + } + }; + // Don't do anything when the cursor is not on top of a bracket. if !is_valid_bracket(bracket) { return None; } // Determine the direction of the matching. - let is_fwd = is_forward_bracket(bracket); + let is_fwd = is_open_bracket(bracket); let chars_iter = if is_fwd { doc.chars_at(cursor_pos + 1) } else { @@ -159,19 +183,7 @@ pub fn find_matching_bracket_plaintext(doc: RopeSlice, cursor_pos: usize) -> Opt for (i, candidate) in chars_iter.take(MAX_PLAINTEXT_SCAN).enumerate() { if candidate == bracket { open_cnt += 1; - } else if is_valid_pair( - doc, - if is_fwd { - cursor_pos - } else { - cursor_pos - i - 1 - }, - if is_fwd { - cursor_pos + i + 1 - } else { - cursor_pos - }, - ) { + } else if candidate == matching_bracket { // Return when all pending brackets have been closed. if open_cnt == 1 { return Some(if is_fwd { @@ -187,15 +199,49 @@ pub fn find_matching_bracket_plaintext(doc: RopeSlice, cursor_pos: usize) -> Opt None } -fn is_valid_bracket(c: char) -> bool { - PAIRS.iter().any(|(l, r)| *l == c || *r == c) +/// Returns the open and closing chars pair. If not found in +/// [`BRACKETS`] returns (ch, ch). +/// +/// ``` +/// use helix_core::match_brackets::get_pair; +/// +/// assert_eq!(get_pair('['), ('[', ']')); +/// assert_eq!(get_pair('}'), ('{', '}')); +/// assert_eq!(get_pair('"'), ('"', '"')); +/// ``` +pub fn get_pair(ch: char) -> (char, char) { + PAIRS + .iter() + .find(|(open, close)| *open == ch || *close == ch) + .copied() + .unwrap_or((ch, ch)) +} + +pub fn is_open_bracket(ch: char) -> bool { + BRACKETS.iter().any(|(l, _)| *l == ch) +} + +pub fn is_close_bracket(ch: char) -> bool { + BRACKETS.iter().any(|(_, r)| *r == ch) +} + +pub fn is_valid_bracket(ch: char) -> bool { + BRACKETS.iter().any(|(l, r)| *l == ch || *r == ch) +} + +pub fn is_open_pair(ch: char) -> bool { + PAIRS.iter().any(|(l, _)| *l == ch) +} + +pub fn is_close_pair(ch: char) -> bool { + PAIRS.iter().any(|(_, r)| *r == ch) } -fn is_forward_bracket(c: char) -> bool { - PAIRS.iter().any(|(l, _)| *l == c) +pub fn is_valid_pair(ch: char) -> bool { + PAIRS.iter().any(|(l, r)| *l == ch || *r == ch) } -fn is_valid_pair(doc: RopeSlice, start_char: usize, end_char: usize) -> bool { +fn is_valid_pair_on_pos(doc: RopeSlice, start_char: usize, end_char: usize) -> bool { PAIRS.contains(&(doc.char(start_char), doc.char(end_char))) } |