diff --git a/lib/binding_rust/bindings.rs b/lib/binding_rust/bindings.rs index da870263..d2239893 100644 --- a/lib/binding_rust/bindings.rs +++ b/lib/binding_rust/bindings.rs @@ -288,6 +288,14 @@ extern "C" { #[doc = " Get the node's language."] pub fn ts_node_language(arg1: TSNode) -> *const TSLanguage; } +extern "C" { + #[doc = " Get the node's type as it appears in the grammar ignoring aliases as a\n null-terminated string."] + pub fn ts_node_grammar_type(arg1: TSNode) -> *const ::std::os::raw::c_char; +} +extern "C" { + #[doc = " Get the node's type as a numerical id as it appears in the grammar ignoring\n aliases. This should be used in `ts_language_next_state` instead of\n `ts_node_symbol`."] + pub fn ts_node_grammar_symbol(arg1: TSNode) -> TSSymbol; +} extern "C" { #[doc = " Get the node's start byte."] pub fn ts_node_start_byte(arg1: TSNode) -> u32; @@ -340,6 +348,10 @@ extern "C" { #[doc = " Get this node's parse state."] pub fn ts_node_parse_state(arg1: TSNode) -> TSStateId; } +extern "C" { + #[doc = " Get the parse state after this node."] + pub fn ts_node_next_parse_state(arg1: TSNode) -> TSStateId; +} extern "C" { #[doc = " Get the node's immediate parent."] pub fn ts_node_parent(arg1: TSNode) -> TSNode; @@ -681,7 +693,7 @@ extern "C" { pub fn ts_language_version(arg1: *const TSLanguage) -> u32; } extern "C" { - #[doc = " Get the next parse state. Combine this with lookahead iterators to generate\n completion suggestions or valid symbols in error nodes."] + #[doc = " Get the next parse state. Combine this with lookahead iterators to generate\n completion suggestions or valid symbols in error nodes. Use\n `ts_node_grammar_symbol` for valid symbols."] pub fn ts_language_next_state( arg1: *const TSLanguage, arg2: TSStateId, diff --git a/lib/binding_rust/lib.rs b/lib/binding_rust/lib.rs index 6a46dafc..cdf1ffcf 100644 --- a/lib/binding_rust/lib.rs +++ b/lib/binding_rust/lib.rs @@ -349,6 +349,11 @@ impl Language { /// Get the next parse state. Combine this with [lookahead_iterator] to /// generate completion suggestions or valid symbols in error nodes. + /// + /// Example: + /// ``` + /// let state = language.next_state(node.parse_state(), node.grammar_id()); + /// ``` #[doc(alias = "ts_language_next_state")] pub fn next_state(&self, state: u16, id: u16) -> u16 { unsafe { ffi::ts_language_next_state(self.0, state, id) } @@ -872,6 +877,13 @@ impl<'tree> Node<'tree> { unsafe { ffi::ts_node_symbol(self.0) } } + /// Get the node's type as a numerical id as it appears in the grammar + /// ignoring aliases. + #[doc(alias = "ts_node_grammar_symbol")] + pub fn grammar_id(&self) -> u16 { + unsafe { ffi::ts_node_grammar_symbol(self.0) } + } + /// Get this node's type as a string. #[doc(alias = "ts_node_type")] pub fn kind(&self) -> &'static str { @@ -880,6 +892,15 @@ impl<'tree> Node<'tree> { .unwrap() } + /// Get this node's symbol name as it appears in the grammar ignoring + /// aliases as a string. + #[doc(alias = "ts_node_grammar_type")] + pub fn grammar_name(&self) -> &'static str { + unsafe { CStr::from_ptr(ffi::ts_node_grammar_type(self.0)) } + .to_str() + .unwrap() + } + /// Get the [Language] that was used to parse this node's syntax tree. #[doc(alias = "ts_node_language")] pub fn language(&self) -> Language { @@ -931,6 +952,12 @@ impl<'tree> Node<'tree> { unsafe { ffi::ts_node_parse_state(self.0) } } + /// Get the parse state after this node. + #[doc(alias = "ts_node_next_parse_state")] + pub fn next_parse_state(&self) -> u16 { + unsafe { ffi::ts_node_next_parse_state(self.0) } + } + /// Check if this node is *missing*. /// /// Missing nodes are inserted by the parser in order to recover from certain kinds of diff --git a/lib/binding_web/binding.c b/lib/binding_web/binding.c index 4ba36f65..5a8e4e34 100644 --- a/lib/binding_web/binding.c +++ b/lib/binding_web/binding.c @@ -361,6 +361,11 @@ uint16_t ts_node_symbol_wasm(const TSTree *tree) { return ts_node_symbol(node); } +uint16_t ts_node_grammar_symbol_wasm(const TSTree *tree) { + TSNode node = unmarshal_node(tree); + return ts_node_grammar_symbol(node); +} + uint32_t ts_node_child_count_wasm(const TSTree *tree) { TSNode node = unmarshal_node(tree); return ts_node_child_count(node); @@ -621,6 +626,11 @@ uint16_t ts_node_parse_state_wasm(const TSTree *tree) { return ts_node_parse_state(node); } +uint16_t ts_node_next_parse_state_wasm(const TSTree *tree) { + TSNode node = unmarshal_node(tree); + return ts_node_next_parse_state(node); +} + /******************/ /* Section - Query */ /******************/ diff --git a/lib/binding_web/binding.js b/lib/binding_web/binding.js index 4203bd92..38efabd6 100644 --- a/lib/binding_web/binding.js +++ b/lib/binding_web/binding.js @@ -209,10 +209,19 @@ class Node { return C._ts_node_symbol_wasm(this.tree[0]); } + get grammarId() { + marshalNode(this); + return C._ts_node_grammar_symbol_wasm(this.tree[0]); + } + get type() { return this.tree.language.types[this.typeId] || 'ERROR'; } + get grammarType() { + return this.tree.language.types[this.grammarId] || 'ERROR'; + } + get endPosition() { marshalNode(this); C._ts_node_end_point_wasm(this.tree[0]); @@ -233,6 +242,11 @@ class Node { return C._ts_node_parse_state_wasm(this.tree[0]); } + get nextParseState() { + marshalNode(this); + return C._ts_node_next_parse_state_wasm(this.tree[0]); + } + isNamed() { marshalNode(this); return C._ts_node_is_named_wasm(this.tree[0]) === 1; diff --git a/lib/binding_web/exports.json b/lib/binding_web/exports.json index ba34eedb..1c638bb0 100644 --- a/lib/binding_web/exports.json +++ b/lib/binding_web/exports.json @@ -59,6 +59,7 @@ "_ts_node_is_missing_wasm", "_ts_node_is_named_wasm", "_ts_node_parse_state_wasm", + "_ts_node_next_parse_state_wasm", "_ts_node_named_child_count_wasm", "_ts_node_named_child_wasm", "_ts_node_named_children_wasm", @@ -72,6 +73,7 @@ "_ts_node_start_index_wasm", "_ts_node_start_point_wasm", "_ts_node_symbol_wasm", + "_ts_node_grammar_symbol_wasm", "_ts_node_to_string_wasm", "_ts_parser_delete", "_ts_parser_enable_logger_wasm", diff --git a/lib/binding_web/test/node-test.js b/lib/binding_web/test/node-test.js index e897d991..1abef756 100644 --- a/lib/binding_web/test/node-test.js +++ b/lib/binding_web/test/node-test.js @@ -327,7 +327,7 @@ describe("Node", () => { }); describe(".parseState", () => { - const text = "10 * 5"; + const text = "10 / 5"; it(`returns node parse state ids`, async () => { tree = await parser.parse(text) @@ -335,9 +335,13 @@ describe("Node", () => { const [numerator, slash, denominator] = quotientNode.children; assert.equal(tree.rootNode.parseState, 0); - assert.equal(numerator.parseState, 1); - assert.equal(slash.parseState, 553); - assert.equal(denominator.parseState, 185); + // parse states will change on any change to the grammar so test that it + // returns something instead + assert.isAbove(numerator.parseState, 0); + assert.isAbove(numerator.nextParseState, 0); + assert.isAbove(slash.parseState, 0); + assert.isAbove(denominator.parseState, 0); + assert.isAbove(denominator.nextParseState, 0); }) }); diff --git a/lib/binding_web/tree-sitter-web.d.ts b/lib/binding_web/tree-sitter-web.d.ts index 709c2023..dfe7766c 100644 --- a/lib/binding_web/tree-sitter-web.d.ts +++ b/lib/binding_web/tree-sitter-web.d.ts @@ -55,11 +55,14 @@ declare module 'web-tree-sitter' { ) => string | null; export interface SyntaxNode { - id: number; + typeId: number; + grammarId: number; tree: Tree; type: string; + grammarType: string; text: string; parseState: number; + nextParseState: number; startPosition: Point; endPosition: Point; startIndex: number; diff --git a/lib/include/tree_sitter/api.h b/lib/include/tree_sitter/api.h index a9c8a182..4bd2f43d 100644 --- a/lib/include/tree_sitter/api.h +++ b/lib/include/tree_sitter/api.h @@ -445,6 +445,19 @@ TSSymbol ts_node_symbol(TSNode); */ const TSLanguage *ts_node_language(TSNode); +/** + * Get the node's type as it appears in the grammar ignoring aliases as a + * null-terminated string. + */ +const char *ts_node_grammar_type(TSNode); + +/** + * Get the node's type as a numerical id as it appears in the grammar ignoring + * aliases. This should be used in `ts_language_next_state` instead of + * `ts_node_symbol`. + */ +TSSymbol ts_node_grammar_symbol(TSNode); + /** * Get the node's start byte. */ @@ -519,6 +532,11 @@ bool ts_node_is_error(TSNode); */ TSStateId ts_node_parse_state(TSNode); +/** + * Get the parse state after this node. +*/ +TSStateId ts_node_next_parse_state(TSNode); + /** * Get the node's immediate parent. */ @@ -1025,7 +1043,8 @@ uint32_t ts_language_version(const TSLanguage *); /** * Get the next parse state. Combine this with lookahead iterators to generate - * completion suggestions or valid symbols in error nodes. + * completion suggestions or valid symbols in error nodes. Use + * `ts_node_grammar_symbol` for valid symbols. */ TSStateId ts_language_next_state(const TSLanguage *, TSStateId, TSSymbol); diff --git a/lib/src/node.c b/lib/src/node.c index 8ef2d150..46028f37 100644 --- a/lib/src/node.c +++ b/lib/src/node.c @@ -429,6 +429,15 @@ const TSLanguage *ts_node_language(TSNode self) { return self.tree->language; } +TSSymbol ts_node_grammar_symbol(TSNode self) { + return ts_subtree_symbol(ts_node__subtree(self)); +} + +const char *ts_node_grammar_type(TSNode self) { + TSSymbol symbol = ts_subtree_symbol(ts_node__subtree(self)); + return ts_language_symbol_name(self.tree->language, symbol); +} + char *ts_node_string(TSNode self) { return ts_subtree_string(ts_node__subtree(self), self.tree->language, false); } @@ -477,6 +486,14 @@ TSStateId ts_node_parse_state(TSNode self) { return ts_subtree_parse_state(ts_node__subtree(self)); } +TSStateId ts_node_next_parse_state(TSNode self) { + const TSLanguage *language = self.tree->language; + uint16_t state = ts_node_parse_state(self); + uint16_t symbol = ts_node_grammar_symbol(self); + + return ts_language_next_state(language, state, symbol); +} + TSNode ts_node_parent(TSNode self) { TSNode node = ts_tree_root_node(self.tree); uint32_t end_byte = ts_node_end_byte(self);