diff --git a/lib/binding_web/binding.c b/lib/binding_web/binding.c index 31efeceb..d5c4150c 100644 --- a/lib/binding_web/binding.c +++ b/lib/binding_web/binding.c @@ -289,6 +289,11 @@ uint32_t ts_tree_cursor_end_index_wasm(const TSTree *tree) { return byte_to_code_unit(ts_node_end_byte(node)); } +uint32_t ts_tree_cursor_current_field_id_wasm(const TSTree *tree) { + TSTreeCursor cursor = unmarshal_cursor(TRANSFER_BUFFER, tree); + return ts_tree_cursor_current_field_id(&cursor); +} + void ts_tree_cursor_current_node_wasm(const TSTree *tree) { TSTreeCursor cursor = unmarshal_cursor(TRANSFER_BUFFER, tree); marshal_node(TRANSFER_BUFFER, ts_tree_cursor_current_node(&cursor)); diff --git a/lib/binding_web/binding.js b/lib/binding_web/binding.js index fad20986..0965145c 100644 --- a/lib/binding_web/binding.js +++ b/lib/binding_web/binding.js @@ -525,6 +525,12 @@ class TreeCursor { return unmarshalNode(this.tree); } + currentFieldName() { + marshalTreeCursor(this); + const fieldId = C._ts_tree_cursor_current_field_id_wasm(this.tree[0]); + return this.tree.language.fields[fieldId]; + } + gotoFirstChild() { marshalTreeCursor(this); const result = C._ts_tree_cursor_goto_first_child_wasm(this.tree[0]); @@ -559,6 +565,15 @@ class Language { this.types[i] = UTF8ToString(C._ts_language_symbol_name(this[0], i)); } } + this.fields = new Array(C._ts_language_field_count(this[0]) + 1); + for (let i = 0, n = this.fields.length; i < n; i++) { + const fieldName = C._ts_language_field_name_for_id(this[0], i); + if (fieldName !== 0) { + this.fields[i] = UTF8ToString(fieldName); + } else { + this.fields[i] = null; + } + } } get version() { diff --git a/lib/binding_web/exports.json b/lib/binding_web/exports.json index 456feb19..8c8c1b40 100644 --- a/lib/binding_web/exports.json +++ b/lib/binding_web/exports.json @@ -30,6 +30,8 @@ "abort", "_ts_init", + "_ts_language_field_count", + "_ts_language_field_name_for_id", "_ts_language_symbol_count", "_ts_language_symbol_name", "_ts_language_symbol_type", @@ -64,6 +66,7 @@ "_ts_parser_new_wasm", "_ts_parser_parse_wasm", "_ts_parser_set_language", + "_ts_tree_cursor_current_field_id_wasm", "_ts_tree_cursor_current_node_id_wasm", "_ts_tree_cursor_current_node_is_missing_wasm", "_ts_tree_cursor_current_node_is_named_wasm", diff --git a/lib/binding_web/test/tree-test.js b/lib/binding_web/test/tree-test.js index 3eaf36f9..2311ce4e 100644 --- a/lib/binding_web/test/tree-test.js +++ b/lib/binding_web/test/tree-test.js @@ -263,6 +263,34 @@ describe("Tree", () => { assert(!cursor.gotoParent()); }); + it('keeps track of the field name associated with each node', () => { + tree = parser.parse('a.b();'); + cursor = tree.walk(); + cursor.gotoFirstChild(); + cursor.gotoFirstChild(); + + assert.equal(cursor.currentNode().type, 'call_expression'); + assert.equal(cursor.currentFieldName(), null); + + cursor.gotoFirstChild(); + assert.equal(cursor.currentNode().type, 'member_expression'); + assert.equal(cursor.currentFieldName(), 'function'); + + cursor.gotoFirstChild(); + assert.equal(cursor.currentNode().type, 'identifier'); + assert.equal(cursor.currentFieldName(), 'object'); + + cursor.gotoNextSibling(); + cursor.gotoNextSibling(); + assert.equal(cursor.currentNode().type, 'property_identifier'); + assert.equal(cursor.currentFieldName(), 'property'); + + cursor.gotoParent(); + cursor.gotoNextSibling(); + assert.equal(cursor.currentNode().type, 'arguments'); + assert.equal(cursor.currentFieldName(), 'arguments'); + }); + it('returns a cursor that can be reset anywhere in the tree', () => { tree = parser.parse('a * b + c / d'); cursor = tree.walk();