@@ -597,6 +597,25 @@ impl Ast {
597597 self . nodes . get ( id. 0 )
598598 }
599599
600+ fn source_range_ignoring_fields (
601+ & self ,
602+ id : Id ,
603+ ignored_fields : & [ & str ] ,
604+ ) -> Option < Range > {
605+ let node = self . get_node ( id) ?;
606+ let source_range = node. source_range ( ) ?;
607+ let ignored_ranges = node
608+ . fields
609+ . iter ( )
610+ . filter ( |( field_id, _) | {
611+ self . field_name_for_id ( * * field_id)
612+ . is_some_and ( |name| ignored_fields. contains ( & name) )
613+ } )
614+ . flat_map ( |( _, children) | children)
615+ . filter_map ( |child| self . get_node ( * child) . and_then ( Node :: source_range) ) ;
616+ Some ( source_range. ignoring_boundary_ranges ( ignored_ranges) )
617+ }
618+
600619 pub fn print ( & self , source : & str , root_id : Id ) -> Value {
601620 let root = & self . nodes ( ) [ root_id. 0 ] ;
602621 self . print_node ( root, source)
@@ -1100,6 +1119,7 @@ pub struct Rule<C = ()> {
11001119 query : QueryNode ,
11011120 guard : Option < Guard < C > > ,
11021121 transform : Transform < C > ,
1122+ ignored_location_fields : Vec < & ' static str > ,
11031123 /// If true, after this rule fires on a node the engine will try to
11041124 /// re-apply this same rule on the result root. Defaults to false:
11051125 /// each rule fires at most once on a given node, which prevents
@@ -1114,6 +1134,7 @@ impl<C> Rule<C> {
11141134 query,
11151135 guard : None ,
11161136 transform,
1137+ ignored_location_fields : Vec :: new ( ) ,
11171138 repeated : false ,
11181139 }
11191140 }
@@ -1125,6 +1146,7 @@ impl<C> Rule<C> {
11251146 query,
11261147 guard : Some ( guard) ,
11271148 transform,
1149+ ignored_location_fields : Vec :: new ( ) ,
11281150 repeated : false ,
11291151 }
11301152 }
@@ -1138,6 +1160,10 @@ impl<C> Rule<C> {
11381160 self
11391161 }
11401162
1163+ fn set_ignored_location_fields ( & mut self , fields : & [ & ' static str ] ) {
1164+ self . ignored_location_fields = fields. to_vec ( ) ;
1165+ }
1166+
11411167 /// Attempt to match this rule's query against `node`, returning the raw
11421168 /// captures on success. Does not evaluate the guard or invoke the
11431169 /// transform.
@@ -1176,10 +1202,8 @@ impl<C> Rule<C> {
11761202 translator : TranslatorHandle < ' _ , C > ,
11771203 ) -> Result < Vec < Id > , String > {
11781204 fresh. next_scope ( ) ;
1179- let source_range = ast. get_node ( node) . and_then ( |n| match n. content {
1180- NodeContent :: Range ( r) => Some ( r) ,
1181- _ => n. source_range ,
1182- } ) ;
1205+ let source_range =
1206+ ast. source_range_ignoring_fields ( node, & self . ignored_location_fields ) ;
11831207 ( self . transform ) ( ast, captures, fresh, source_range, user_ctx, translator)
11841208 }
11851209}
@@ -1465,6 +1489,9 @@ pub struct DesugaringConfig<C = ()> {
14651489 /// node types are used (i.e. the desugared AST has the same node types
14661490 /// as the tree-sitter grammar).
14671491 pub output_node_types_yaml : Option < & ' static str > ,
1492+ /// Input field names whose boundary ranges are excluded from rule-result
1493+ /// locations.
1494+ pub ignored_location_fields : Vec < & ' static str > ,
14681495}
14691496
14701497// Manual `Default` impl so users with a custom `C` that doesn't implement
@@ -1474,6 +1501,7 @@ impl<C> Default for DesugaringConfig<C> {
14741501 Self {
14751502 phases : Vec :: new ( ) ,
14761503 output_node_types_yaml : None ,
1504+ ignored_location_fields : Vec :: new ( ) ,
14771505 }
14781506 }
14791507}
@@ -1490,12 +1518,30 @@ impl<C> DesugaringConfig<C> {
14901518 mut self ,
14911519 name : impl Into < String > ,
14921520 kind : PhaseKind ,
1493- rules : Vec < Rule < C > > ,
1521+ mut rules : Vec < Rule < C > > ,
14941522 ) -> Self {
1523+ for rule in & mut rules {
1524+ rule. set_ignored_location_fields ( & self . ignored_location_fields ) ;
1525+ }
14951526 self . phases . push ( Phase :: new ( name, kind, rules) ) ;
14961527 self
14971528 }
14981529
1530+ /// Ignore boundary syntax stored under any of these input field names when
1531+ /// calculating matched locations for rule results.
1532+ pub fn with_ignored_location_fields (
1533+ mut self ,
1534+ fields : impl IntoIterator < Item = & ' static str > ,
1535+ ) -> Self {
1536+ self . ignored_location_fields = fields. into_iter ( ) . collect ( ) ;
1537+ for phase in & mut self . phases {
1538+ for rule in & mut phase. rules {
1539+ rule. set_ignored_location_fields ( & self . ignored_location_fields ) ;
1540+ }
1541+ }
1542+ self
1543+ }
1544+
14991545 pub fn with_output_node_types_yaml ( mut self , yaml : & ' static str ) -> Self {
15001546 self . output_node_types_yaml = Some ( yaml) ;
15011547 self
0 commit comments