@@ -442,9 +442,6 @@ impl SyntaxNode {
442
442
/// Find a token in the subtree corresponding to this node, which covers the offset.
443
443
/// Precondition: offset must be withing node's range.
444
444
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.
448
445
let range = self . text_range ( ) ;
449
446
assert ! (
450
447
range. start( ) <= offset && offset <= range. end( ) ,
@@ -456,25 +453,37 @@ impl SyntaxNode {
456
453
return TokenAtOffset :: None ;
457
454
}
458
455
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
+ }
473
484
}
474
- _ => unreachable ! ( ) ,
475
485
}
476
- } else {
477
- left. token_at_offset ( offset)
486
+ unreachable ! ( )
478
487
}
479
488
}
480
489
@@ -796,3 +805,62 @@ fn filter_nodes<'a, I: Iterator<Item = (GreenElementRef<'a>, T)>, T>(
796
805
NodeOrToken :: Token ( _) => None ,
797
806
} )
798
807
}
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