Repository: helix-editor/helix
File: helix-core/src/surround.rs (Rust)

Code before:
        .copied()
        .unwrap_or((ch, ch))
}

/// Find the position of surround pairs of any [`PAIRS`] using tree-sitter when possible.
///
/// Returns a tuple `(anchor, head)`, meaning it is not always ordered.
pub fn find_nth_closest_pairs_pos(
    syntax: Option<&Syntax>,
    text: RopeSlice,
    range: Range,
    skip: usize,
) -> Result<(usize, usize)> {
    match syntax {
        Some(syntax) => find_nth_closest_pairs_ts(syntax, text, range, skip),
        None => find_nth_closest_pairs_plain(text, range, skip),
    }
}

fn find_nth_closest_pairs_ts(
    syntax: &Syntax,
    text: RopeSlice,
    range: Range,
    skip: usize,
) -> Result<(usize, usize)> {
    let is_close_pair = |ch| PAIRS.iter().any(|(_, close)| *close == ch);

    let cursor = range.cursor(text);
    let Some(mut closing) = find_matching_bracket_fuzzy(syntax, text, cursor) else {
        return Err(Error::PairNotFound);
    };

    for _ in 1..skip {
        let next = next_grapheme_boundary(text, closing);
        // If we have two closing chars in a row, `next_grapheme_boundary` will find next closing
        closing = if is_close_pair(text.char(next)) {
            next
        } else if let Some(idx) = find_matching_bracket_fuzzy(syntax, text, next) {
            idx
        } else {
            return Err(Error::PairNotFound);
        }
    }
    match find_matching_bracket(syntax, text, closing) {
        Some(opening) => {
            if range.head < range.anchor {
                Ok((closing, opening))
            } else {
                Ok((opening, closing))
            }
        }

Review comment (suggestion):
This will case issues because the list of pairs in this file and match_braclet are different for historical reasons. I think thus would be a good opportunity to sync the two (and delete one of the constants so they don't go put of sync again)

Code after:
    range: Range,
    mut skip: usize,
) -> Result<(usize, usize)> {
    let mut opening = range.from();
    // We want to expand the selection if we are already on the found pair,
    // otherwise we would need to subtract "-1" from "range.to()".
    let mut closing = range.to();

    while skip > 0 {
        closing = find_matching_bracket_fuzzy(syntax, text, closing).ok_or(Error::PairNotFound)?;
        opening = find_matching_bracket(syntax, text, closing).ok_or(Error::PairNotFound)?;
        // If we're already on a closing bracket "find_matching_bracket_fuzzy" will return
        // the position of the opening bracket.
        if closing < opening {
            (opening, closing) = (closing, opening);
        }

        // In case found brackets are partially inside current selection.
        if range.from() < opening || closing < range.to() - 1 {
            closing = next_grapheme_boundary(text, closing);
        } else {
            skip -= 1;
            if skip != 0 {
                closing = next_grapheme_boundary(text, closing);
            }
        }
    }

    // Keep the original direction.
    if let Direction::Forward = range.direction() {
        Ok((opening, closing))
    } else {
        Ok((closing, opening))
    }
}

fn find_nth_closest_pairs_plain(
    text: RopeSlice,
    range: Range,
    mut skip: usize,
) -> Result<(usize, usize)> {
    let mut stack = Vec::with_capacity(2);
    let pos = range.from();
    let mut close_pos = pos.saturating_sub(1);

    for ch in text.chars_at(pos) {
        close_pos += 1;

        if is_open_bracket(ch) {
            // Track open pairs encountered so that we can step over
            // the corresponding close pairs that will come up further