@@ -672,3 +672,60 @@ func shouldSkipChild(node *ast.Node) bool {
672672 ast .IsJSDocLinkLike (node ) ||
673673 ast .IsJSDocTag (node )
674674}
675+
676+ // FindChildOfKind searches for a child node or token of the specified kind within a containing node.
677+ // This function scans through both AST nodes and intervening tokens to find the first match.
678+ func FindChildOfKind (containingNode * ast.Node , kind ast.Kind , sourceFile * ast.SourceFile ) * ast.Node {
679+ lastNodePos := containingNode .Pos ()
680+ scan := scanner .GetScannerForSourceFile (sourceFile , lastNodePos )
681+
682+ var foundChild * ast.Node
683+ visitNode := func (node * ast.Node ) bool {
684+ if node == nil || node .Flags & ast .NodeFlagsReparsed != 0 {
685+ return false
686+ }
687+ // Look for child in preceding tokens.
688+ startPos := lastNodePos
689+ for startPos < node .Pos () {
690+ tokenKind := scan .Token ()
691+ tokenFullStart := scan .TokenFullStart ()
692+ tokenEnd := scan .TokenEnd ()
693+ token := sourceFile .GetOrCreateToken (tokenKind , tokenFullStart , tokenEnd , containingNode )
694+ if tokenKind == kind {
695+ foundChild = token
696+ return true
697+ }
698+ startPos = tokenEnd
699+ scan .Scan ()
700+ }
701+ if node .Kind == kind {
702+ foundChild = node
703+ return true
704+ }
705+
706+ lastNodePos = node .End ()
707+ scan .ResetPos (lastNodePos )
708+ return false
709+ }
710+
711+ ast .ForEachChildAndJSDoc (containingNode , sourceFile , visitNode )
712+
713+ if foundChild != nil {
714+ return foundChild
715+ }
716+
717+ // Look for child in trailing tokens.
718+ startPos := lastNodePos
719+ for startPos < containingNode .End () {
720+ tokenKind := scan .Token ()
721+ tokenFullStart := scan .TokenFullStart ()
722+ tokenEnd := scan .TokenEnd ()
723+ token := sourceFile .GetOrCreateToken (tokenKind , tokenFullStart , tokenEnd , containingNode )
724+ if tokenKind == kind {
725+ return token
726+ }
727+ startPos = tokenEnd
728+ scan .Scan ()
729+ }
730+ return nil
731+ }
0 commit comments