diff --git a/lib/binding_web/binding.c b/lib/binding_web/binding.c index 02d91738..e6018b03 100644 --- a/lib/binding_web/binding.c +++ b/lib/binding_web/binding.c @@ -331,6 +331,11 @@ void ts_node_named_child_wasm(const TSTree *tree, uint32_t index) { marshal_node(TRANSFER_BUFFER, ts_node_named_child(node, index)); } +void ts_node_child_by_field_id_wasm(const TSTree *tree, uint32_t field_id) { + TSNode node = unmarshal_node(tree); + marshal_node(TRANSFER_BUFFER, ts_node_child_by_field_id(node, field_id)); +} + void ts_node_next_sibling_wasm(const TSTree *tree) { TSNode node = unmarshal_node(tree); marshal_node(TRANSFER_BUFFER, ts_node_next_sibling(node)); diff --git a/lib/binding_web/binding.js b/lib/binding_web/binding.js index 5aff34fe..4ce334cc 100644 --- a/lib/binding_web/binding.js +++ b/lib/binding_web/binding.js @@ -302,6 +302,17 @@ class Node { return unmarshalNode(this.tree); } + childForFieldId(fieldId) { + marshalNode(this); + C._ts_node_child_by_field_id_wasm(this.tree[0], fieldId); + return unmarshalNode(this.tree); + } + + childForFieldName(fieldName) { + const fieldId = this.tree.language.fields.indexOf(fieldName); + if (fieldId !== -1) return this.childForFieldId(fieldId); + } + get childCount() { marshalNode(this); return C._ts_node_child_count_wasm(this.tree[0]); @@ -586,10 +597,13 @@ class TreeCursor { return unmarshalNode(this.tree); } - currentFieldName() { + currentFieldId() { marshalTreeCursor(this); - const fieldId = C._ts_tree_cursor_current_field_id_wasm(this.tree[0]); - return this.tree.language.fields[fieldId]; + return C._ts_tree_cursor_current_field_id_wasm(this.tree[0]); + } + + currentFieldName() { + return this.tree.language.fields[this.currentFieldId()]; } gotoFirstChild() { @@ -641,6 +655,23 @@ class Language { return C._ts_language_version(this[0]); } + get fieldCount() { + return this.fields.length - 1; + } + + fieldIdForName(fieldName) { + const result = this.fields.indexOf(fieldName); + if (result !== -1) { + return result; + } else { + return null; + } + } + + fieldNameForId(fieldId) { + return this.fields[fieldName] || null; + } + static load(url) { let bytes; if ( diff --git a/lib/binding_web/exports.json b/lib/binding_web/exports.json index 10f1aa25..a0cf9305 100644 --- a/lib/binding_web/exports.json +++ b/lib/binding_web/exports.json @@ -36,6 +36,7 @@ "_ts_language_symbol_name", "_ts_language_symbol_type", "_ts_language_version", + "_ts_node_child_by_field_id_wasm", "_ts_node_child_count_wasm", "_ts_node_child_wasm", "_ts_node_children_wasm", diff --git a/lib/binding_web/test/node-test.js b/lib/binding_web/test/node-test.js index d1badd74..933ff38f 100644 --- a/lib/binding_web/test/node-test.js +++ b/lib/binding_web/test/node-test.js @@ -116,6 +116,35 @@ describe("Node", () => { }) }); + describe('.childForFieldName()', () => { + it('returns null when the node has no children', () => { + tree = parser.parse("class A { b() {} }"); + + const classNode = tree.rootNode.firstChild; + assert.equal(classNode.type, 'class_declaration'); + + const classNameNode = classNode.childForFieldName('name'); + assert.equal(classNameNode.type, 'identifier'); + assert.equal(classNameNode.text, 'A'); + + const bodyNode = classNode.childForFieldName('body'); + assert.equal(bodyNode.type, 'class_body'); + assert.equal(bodyNode.text, '{ b() {} }'); + + const methodNode = bodyNode.firstNamedChild; + assert.equal(methodNode.type, 'method_definition'); + assert.equal(methodNode.text, 'b() {}'); + + const methodNameNode = methodNode.childForFieldName('name'); + assert.equal(methodNameNode.type, 'property_identifier'); + assert.equal(methodNameNode.text, 'b'); + + const paramsNode = methodNode.childForFieldName('parameters'); + assert.equal(paramsNode.type, 'formal_parameters'); + assert.equal(paramsNode.text, '()'); + }); + }); + describe(".nextSibling and .previousSibling", () => { it("returns the node's next and previous sibling", () => { tree = parser.parse("x10 + 1000"); diff --git a/lib/binding_web/tree-sitter-web.d.ts b/lib/binding_web/tree-sitter-web.d.ts index dc2d629f..26d2b073 100644 --- a/lib/binding_web/tree-sitter-web.d.ts +++ b/lib/binding_web/tree-sitter-web.d.ts @@ -71,6 +71,8 @@ declare module 'web-tree-sitter' { toString(): string; child(index: number): SyntaxNode | null; namedChild(index: number): SyntaxNode | null; + childForFieldId(fieldId: number): SyntaxNode | null; + childForFieldName(fieldName: string): SyntaxNode | null; descendantForIndex(index: number): SyntaxNode; descendantForIndex(startIndex: number, endIndex: number): SyntaxNode; @@ -97,6 +99,7 @@ declare module 'web-tree-sitter' { reset(node: SyntaxNode): void; delete(): void; currentNode(): SyntaxNode; + currentFieldId(): number; currentFieldName(): string; gotoParent(): boolean; gotoFirstChild(): boolean; @@ -115,8 +118,15 @@ declare module 'web-tree-sitter' { getEditedRange(other: Tree): Range; getLanguage(): any; } - namespace Language { - function load(url: string): Promise + + class Language { + static load(): Promise; + + readonly version: number; + readonly fieldCount: number; + + fieldNameForId(fieldId: number): string | null + fieldIdForName(fieldName: string): number | null } }