(sourceFile *ast.SourceFile, position int, startNode *ast.Node, excludeJSDoc bool)
| 330 | } |
| 331 | |
| 332 | func FindPrecedingTokenEx(sourceFile *ast.SourceFile, position int, startNode *ast.Node, excludeJSDoc bool) *ast.Node { |
| 333 | var find func(node *ast.Node) *ast.Node |
| 334 | find = func(n *ast.Node) *ast.Node { |
| 335 | if ast.IsNonWhitespaceToken(n) && n.Kind != ast.KindEndOfFile { |
| 336 | return n |
| 337 | } |
| 338 | |
| 339 | // `foundChild` is the leftmost node that contains the target position. |
| 340 | // `prevChild` is the last visited child of the current node. |
| 341 | var foundChild, prevChild *ast.Node |
| 342 | visitNode := func(node *ast.Node, _ *ast.NodeVisitor) *ast.Node { |
| 343 | // skip synthesized nodes (that will exist now because of jsdoc handling) |
| 344 | if node == nil || node.Flags&ast.NodeFlagsReparsed != 0 { |
| 345 | return node |
| 346 | } |
| 347 | if foundChild != nil { // We cannot abort visiting children, so once the desired child is found, we do nothing. |
| 348 | return node |
| 349 | } |
| 350 | if position < node.End() && (prevChild == nil || prevChild.End() <= position) { |
| 351 | foundChild = node |
| 352 | } else { |
| 353 | prevChild = node |
| 354 | } |
| 355 | return node |
| 356 | } |
| 357 | visitNodes := func(nodeList *ast.NodeList, _ *ast.NodeVisitor) *ast.NodeList { |
| 358 | if foundChild != nil { |
| 359 | return nodeList |
| 360 | } |
| 361 | if nodeList != nil && len(nodeList.Nodes) > 0 { |
| 362 | nodes := nodeList.Nodes |
| 363 | index, match := core.BinarySearchUniqueFunc(nodes, func(middle int, _ *ast.Node) int { |
| 364 | // synthetic jsdoc nodes should have jsdocNode.End() <= n.Pos() |
| 365 | if nodes[middle].Flags&ast.NodeFlagsReparsed != 0 { |
| 366 | return comparisonLessThan |
| 367 | } |
| 368 | if position < nodes[middle].End() { |
| 369 | if middle == 0 || position >= nodes[middle-1].End() { |
| 370 | return comparisonEqualTo |
| 371 | } |
| 372 | return comparisonGreaterThan |
| 373 | } |
| 374 | return comparisonLessThan |
| 375 | }) |
| 376 | |
| 377 | if match { |
| 378 | foundChild = nodes[index] |
| 379 | } |
| 380 | |
| 381 | validLookupIndex := core.IfElse(match, index-1, len(nodes)-1) |
| 382 | for i := validLookupIndex; i >= 0; i-- { |
| 383 | if nodes[i].Flags&ast.NodeFlagsReparsed != 0 { |
| 384 | continue |
| 385 | } |
| 386 | if prevChild == nil { |
| 387 | prevChild = nodes[i] |
| 388 | } |
| 389 | } |
no test coverage detected