use tree_sitter::{Point, Tree};
use super::engine::{byte_to_char_col, char_to_byte_col};
pub(super) fn matching(
source: &str,
tree: &Tree,
row: usize,
col_chars: usize,
) -> Option<(usize, usize)> {
let line = source.lines().nth(row)?;
let byte_col = char_to_byte_col(source, row, col_chars);
let ch = line.get(byte_col..).and_then(|s| s.chars().next())?;
let ch_byte_len = ch.len_utf8();
let start = Point {
row,
column: byte_col,
};
let end = Point {
row,
column: byte_col + ch_byte_len,
};
let node = tree.root_node().descendant_for_point_range(start, end)?;
let cursor = Point {
row,
column: byte_col,
};
match ch {
'"' | '\'' | '`' => match_quote(source, node, ch, cursor),
_ => match_bracket(source, node),
}
}
fn match_bracket(source: &str, node: tree_sitter::Node) -> Option<(usize, usize)> {
let (target, want_last) = match node.kind() {
"(" => (")", true),
"[" => ("]", true),
"{" => ("}", true),
"<" => (">", true),
")" => ("(", false),
"]" => ("[", false),
"}" => ("{", false),
">" => ("<", false),
_ => return None,
};
let parent = node.parent()?;
let mut found: Option<tree_sitter::Node> = None;
let mut walk = parent.walk();
for child in parent.children(&mut walk) {
if child.kind() == target {
found = Some(child);
if !want_last {
break;
}
}
}
let m = found?;
let pos = m.start_position();
Some((pos.row, byte_to_char_col(source, pos.row, pos.column)))
}
fn match_quote(
source: &str,
leaf: tree_sitter::Node,
ch: char,
cursor: Point,
) -> Option<(usize, usize)> {
let ch_len = ch.len_utf8();
let mut node = leaf;
loop {
let ns = node.start_byte();
let ne = node.end_byte();
if ne >= ns + 2 * ch_len {
let first = source.get(ns..ne).and_then(|s| s.chars().next());
let last = source.get(ns..ne).and_then(|s| s.chars().next_back());
if first == Some(ch) && last == Some(ch) {
let opener = node.start_position();
let end = node.end_position();
let closer = Point {
row: end.row,
column: end.column.saturating_sub(ch_len),
};
let target = if cursor == opener {
closer
} else if cursor == closer {
opener
} else {
return None;
};
return Some((target.row, byte_to_char_col(source, target.row, target.column)));
}
}
node = node.parent()?;
}
}