(...parentTypes: string[])
| 38 | const toPosition = (point: Point) => new Position(point.row, point.column); |
| 39 | |
| 40 | export const argumentNodeFinder = (...parentTypes: string[]): NodeFinder => { |
| 41 | const left = ["(", "{", "["]; |
| 42 | const right = [")", "}", "]"]; |
| 43 | const delimiters = left.concat(right); |
| 44 | const isType = (node: SyntaxNode | null, typeNames: string[]) => |
| 45 | node != null && typeNames.includes(node.type); |
| 46 | const isOk = (node: SyntaxNode | null) => |
| 47 | node != null && !isType(node, delimiters); |
| 48 | return (node: SyntaxNode, selection?: Selection) => { |
| 49 | let resultNode: SyntaxNode | null; |
| 50 | const { start, end } = selection!; |
| 51 | // Is already child |
| 52 | if (isType(node.parent, parentTypes)) { |
| 53 | if (isType(node, left)) { |
| 54 | resultNode = node.nextNamedSibling; |
| 55 | } else if (isType(node, right)) { |
| 56 | resultNode = node.previousNamedSibling; |
| 57 | } else if (node.type === ",") { |
| 58 | resultNode = end.isBeforeOrEqual(toPosition(node.startPosition)) |
| 59 | ? node.previousNamedSibling |
| 60 | : node.nextNamedSibling; |
| 61 | } else { |
| 62 | resultNode = node; |
| 63 | } |
| 64 | return isOk(resultNode) ? resultNode : null; |
| 65 | // Is parent |
| 66 | } else if (isType(node, parentTypes)) { |
| 67 | const children = [...node.children]; |
| 68 | const childRight = |
| 69 | children.find(({ startPosition }) => |
| 70 | toPosition(startPosition).isAfterOrEqual(end) |
| 71 | ) ?? null; |
| 72 | if (isOk(childRight)) { |
| 73 | return childRight; |
| 74 | } |
| 75 | children.reverse(); |
| 76 | const childLeft = |
| 77 | children.find(({ endPosition }) => |
| 78 | toPosition(endPosition).isBeforeOrEqual(start) |
| 79 | ) ?? null; |
| 80 | if (isOk(childLeft)) { |
| 81 | return childLeft; |
| 82 | } |
| 83 | } |
| 84 | return null; |
| 85 | }; |
| 86 | }; |
| 87 | |
| 88 | /** |
| 89 | * Creates a matcher that can match potentially wrapped nodes. For example |
no test coverage detected