@ -1,3 +1,5 @@
use tree_sitter ::Node ;
use crate ::{ Rope , Syntax } ;
const PAIRS : & [ ( char , char ) ] = & [
@ -6,50 +8,85 @@ const PAIRS: &[(char, char)] = &[
( '[' , ']' ) ,
( '<' , '>' ) ,
( '\'' , '\'' ) ,
( ' "', ' "') ,
( ' \ "', ' \ "') ,
] ;
// limit matching pairs to only ( ) { } [ ] < >
// Returns the position of the matching bracket under cursor.
//
// If the cursor is one the opening bracket, the position of
// the closing bracket is returned. If the cursor in the closing
// bracket, the position of the opening bracket is returned.
//
// If the cursor is not on a bracket, `None` is returned.
#[ must_use ]
pub fn find ( syntax : & Syntax , doc : & Rope , pos : usize ) -> Option < usize > {
let tree = syntax . tree ( ) ;
pub fn find_matching_bracket ( syntax : & Syntax , doc : & Rope , pos : usize ) -> Option < usize > {
if pos > = doc . len_chars ( ) | | ! is_valid_bracket ( doc . char ( pos ) ) {
return None ;
}
find_pair ( syntax , doc , pos , false )
}
let byte_pos = doc . char_to_byte ( pos ) ;
// Returns the position of the bracket that is closing the current scope.
//
// If the cursor is on an opening or closing bracket, the function
// behaves equivalent to [`find_matching_bracket`].
//
// If the cursor position is within a scope, the function searches
// for the surrounding scope that is surrounded by brackets and
// returns the position of the closing bracket for that scope.
//
// If no surrounding scope is found, the function returns `None`.
#[ must_use ]
pub fn find_matching_bracket_fuzzy ( syntax : & Syntax , doc : & Rope , pos : usize ) -> Option < usize > {
find_pair ( syntax , doc , pos , true )
}
// most naive implementation: find the innermost syntax node, if we're at the edge of a node,
// return the other edge.
fn find_pair ( syntax : & Syntax , doc : & Rope , pos : usize , traverse_parents : bool ) -> Option < usize > {
let tree = syntax . tree ( ) ;
let pos = doc . char_to_byte ( pos ) ;
let node = match tree
. root_node ( )
. named_descendant_for_byte_range ( byte_pos , byte_pos )
{
Some ( node ) = > node ,
None = > return None ,
} ;
let mut node = tree . root_node ( ) . named_descendant_for_byte_range ( pos , pos ) ? ;
if node . is_error ( ) {
return None ;
loop {
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 end_byte = = pos {
return Some ( start_char ) ;
}
// We return the end char if the cursor is either on the start char
// or at some arbitrary position between start and end char.
return Some ( end_char ) ;
}
let len = doc . len_bytes ( ) ;
let start_byte = node . start_byte ( ) ;
let end_byte = node . end_byte ( ) . saturating_sub ( 1 ) ; // it's end exclusive
if start_byte > = len | | end_byte > = len {
if traverse_parents {
node = node . parent ( ) ? ;
} else {
return None ;
}
}
}
let start_char = doc . byte_to_char ( start_byte ) ;
let end_char = doc . byte_to_char ( end_byte ) ;
if PAIRS . contains ( & ( doc . char ( start_char ) , doc . char ( end_char ) ) ) {
if start_byte = = byte_pos {
return Some ( end_char ) ;
fn is_valid_bracket ( c : char ) -> bool {
PAIRS . iter ( ) . any ( | ( l , r ) | * l = = c | | * r = = c )
}
if end_byte = = byte_pos {
return Some ( start_char ) ;
fn is_valid_pair ( doc : & Rope , start_char : usize , end_char : usize ) -> bool {
PAIRS . contains ( & ( doc . char ( start_char ) , doc . char ( end_char ) ) )
}
fn surrounding_bytes ( doc : & Rope , node : & Node ) -> Option < ( usize , usize ) > {
let len = doc . len_bytes ( ) ;
let start_byte = node . start_byte ( ) ;
let end_byte = node . end_byte ( ) . saturating_sub ( 1 ) ;
if start_byte > = len | | end_byte > = len {
return None ;
}
None
Some( ( start_byte , end_byte ) )
}