@@ -442,9 +442,6 @@ impl SyntaxNode {
442442 /// Find a token in the subtree corresponding to this node, which covers the offset.
443443 /// Precondition: offset must be withing node's range.
444444 pub fn token_at_offset ( & self , offset : TextUnit ) -> TokenAtOffset < SyntaxToken > {
445- // TODO: this could be faster if we first drill-down to node, and only
446- // then switch to token search. We should also replace explicit
447- // recursion with a loop.
448445 let range = self . text_range ( ) ;
449446 assert ! (
450447 range. start( ) <= offset && offset <= range. end( ) ,
@@ -456,25 +453,37 @@ impl SyntaxNode {
456453 return TokenAtOffset :: None ;
457454 }
458455
459- let mut children = self . children_with_tokens ( ) . filter ( |child| {
460- let child_range = child. text_range ( ) ;
461- !child_range. is_empty ( )
462- && ( child_range. start ( ) <= offset && offset <= child_range. end ( ) )
463- } ) ;
464-
465- let left = children. next ( ) . unwrap ( ) ;
466- let right = children. next ( ) ;
467- assert ! ( children. next( ) . is_none( ) ) ;
468-
469- if let Some ( right) = right {
470- match ( left. token_at_offset ( offset) , right. token_at_offset ( offset) ) {
471- ( TokenAtOffset :: Single ( left) , TokenAtOffset :: Single ( right) ) => {
472- TokenAtOffset :: Between ( left, right)
456+ let mut container_element = self . clone ( ) ;
457+ ' outer: loop {
458+ for child in container_element. children_with_tokens ( ) {
459+ match child {
460+ SyntaxElement :: Token ( token) => {
461+ let range: TextRange = token. text_range ( ) ;
462+ if range. contains_inclusive ( offset) {
463+ return if offset == range. start ( ) {
464+ match token. prev_token ( ) {
465+ Some ( prev_token) => TokenAtOffset :: Between ( prev_token, token) ,
466+ None => TokenAtOffset :: Single ( token) ,
467+ }
468+ } else if offset == range. end ( ) {
469+ match token. next_token ( ) {
470+ Some ( next_token) => TokenAtOffset :: Between ( token, next_token) ,
471+ None => TokenAtOffset :: Single ( token) ,
472+ }
473+ } else {
474+ TokenAtOffset :: Single ( token)
475+ } ;
476+ }
477+ }
478+ SyntaxElement :: Node ( node) => {
479+ if node. text_range ( ) . contains_inclusive ( offset) {
480+ container_element = node;
481+ continue ' outer;
482+ }
483+ }
473484 }
474- _ => unreachable ! ( ) ,
475485 }
476- } else {
477- left. token_at_offset ( offset)
486+ unreachable ! ( )
478487 }
479488 }
480489
@@ -796,3 +805,62 @@ fn filter_nodes<'a, I: Iterator<Item = (GreenElementRef<'a>, T)>, T>(
796805 NodeOrToken :: Token ( _) => None ,
797806 } )
798807}
808+
809+ #[ cfg( test) ]
810+ mod tests {
811+ use crate :: green:: GreenNodeBuilder ;
812+
813+ use super :: * ;
814+
815+ fn node (
816+ builder : & mut GreenNodeBuilder ,
817+ create_children : impl FnOnce ( & mut GreenNodeBuilder ) -> ( ) ,
818+ ) {
819+ builder. start_node ( SyntaxKind ( 4 ) ) ;
820+ create_children ( builder) ;
821+ builder. finish_node ( ) ;
822+ }
823+
824+ fn token ( builder : & mut GreenNodeBuilder , text : & str ) {
825+ builder. token ( SyntaxKind ( 7 ) , SmolStr :: from ( text) )
826+ }
827+
828+ #[ test]
829+ fn token_at_offset_with_empty_token ( ) {
830+ let mut builder = GreenNodeBuilder :: new ( ) ;
831+ node ( & mut builder, |b| {
832+ node ( b, |b| token ( b, "A" ) ) ;
833+ token ( b, "+" ) ;
834+ node ( b, |b| {
835+ node ( b, |b| token ( b, "" ) ) ;
836+ token ( b, "*" ) ;
837+ node ( b, |b| token ( b, "C" ) ) ;
838+ } ) ;
839+ } ) ;
840+ let root = SyntaxNode :: new_root ( builder. finish ( ) ) ;
841+
842+ fn check ( actual : TokenAtOffset < SyntaxToken > , expected : TokenAtOffset < & str > ) {
843+ match ( actual, expected) {
844+ (
845+ TokenAtOffset :: Between ( actual_left, actual_right) ,
846+ TokenAtOffset :: Between ( expected_left, expected_right) ,
847+ ) => {
848+ assert_eq ! ( actual_left. text( ) , expected_left) ;
849+ assert_eq ! ( actual_right. text( ) , expected_right) ;
850+ }
851+ ( TokenAtOffset :: Single ( actual) , TokenAtOffset :: Single ( expected) ) => {
852+ assert_eq ! ( actual. text( ) , expected) ;
853+ }
854+ ( actual, _) => {
855+ assert ! ( false , "unexpected {:#?}" , actual) ;
856+ }
857+ }
858+ }
859+
860+ check ( root. token_at_offset ( 0 . into ( ) ) , TokenAtOffset :: Single ( "A" ) ) ;
861+ check ( root. token_at_offset ( 1 . into ( ) ) , TokenAtOffset :: Between ( "A" , "+" ) ) ;
862+ check ( root. token_at_offset ( 2 . into ( ) ) , TokenAtOffset :: Between ( "+" , "" ) ) ;
863+ check ( root. token_at_offset ( 3 . into ( ) ) , TokenAtOffset :: Between ( "*" , "C" ) ) ;
864+ check ( root. token_at_offset ( 4 . into ( ) ) , TokenAtOffset :: Single ( "C" ) ) ;
865+ }
866+ }
0 commit comments