|
1 | 1 | import { Range } from 'vscode-languageserver/lib/main'
|
2 |
| -import { SyntaxNode } from 'web-tree-sitter' |
| 2 | +import { Point, SyntaxNode } from 'web-tree-sitter' |
3 | 3 |
|
4 | 4 | export function forEach(node: SyntaxNode, cb: (n: SyntaxNode) => void) {
|
5 | 5 | cb(node)
|
@@ -52,3 +52,56 @@ export function findParent(
|
52 | 52 | }
|
53 | 53 | return null
|
54 | 54 | }
|
| 55 | + |
| 56 | +/** |
| 57 | + * Given a tree and a point, try to find the named leaf node that the point corresponds to. |
| 58 | + * This is a helper for wordAtPoint, useful in cases where the point occurs at the boundary of |
| 59 | + * a word so the normal behavior of "namedDescendantForPosition" does not find the desired leaf. |
| 60 | + * For example, if you do |
| 61 | + * > (new Parser()).setLanguage(bash).parse("echo 42").rootNode.descendantForIndex(4).text |
| 62 | + * then you get 'echo 42', not the leaf node for 'echo'. |
| 63 | + * |
| 64 | + * TODO: the need for this function might reveal a flaw in tree-sitter-bash. |
| 65 | + */ |
| 66 | +export function namedLeafDescendantForPosition( |
| 67 | + point: Point, |
| 68 | + rootNode: SyntaxNode, |
| 69 | +): SyntaxNode | null { |
| 70 | + const node = rootNode.namedDescendantForPosition(point) |
| 71 | + |
| 72 | + if (node.childCount === 0) { |
| 73 | + return node |
| 74 | + } else { |
| 75 | + // The node wasn't a leaf. Try to figure out what word we should use. |
| 76 | + const nodeToUse = searchForLeafNode(point, node) |
| 77 | + if (nodeToUse) { |
| 78 | + return nodeToUse |
| 79 | + } else { |
| 80 | + return null |
| 81 | + } |
| 82 | + } |
| 83 | +} |
| 84 | + |
| 85 | +function searchForLeafNode(point: Point, parent: SyntaxNode): SyntaxNode | null { |
| 86 | + let child: SyntaxNode = parent.firstNamedChild |
| 87 | + while (child) { |
| 88 | + if ( |
| 89 | + pointsEqual(child.startPosition, point) || |
| 90 | + pointsEqual(child.endPosition, point) |
| 91 | + ) { |
| 92 | + if (child.childCount === 0) { |
| 93 | + return child |
| 94 | + } else { |
| 95 | + return searchForLeafNode(point, child) |
| 96 | + } |
| 97 | + } |
| 98 | + |
| 99 | + child = child.nextNamedSibling |
| 100 | + } |
| 101 | + |
| 102 | + return null |
| 103 | +} |
| 104 | + |
| 105 | +function pointsEqual(point1: Point, point2: Point) { |
| 106 | + return point1.row === point2.row && point1.column === point2.column |
| 107 | +} |
0 commit comments