From 45fa028201ef8bb8b97e38a509f4e36ad2df5384 Mon Sep 17 00:00:00 2001 From: Amaan Qureshi Date: Sun, 5 Jan 2025 22:06:33 -0500 Subject: [PATCH] feat(web): add missing API functions Co-authored-by: Will Lillis --- lib/binding_web/binding.c | 44 +++++++- lib/binding_web/binding.js | 140 +++++++++++++++++++++++++- lib/binding_web/exports.txt | 10 ++ lib/binding_web/imports.js | 14 +++ lib/binding_web/test/helper.js | 1 + lib/binding_web/test/language-test.js | 7 ++ lib/binding_web/test/node-test.js | 62 ++++++++++-- lib/binding_web/test/parser-test.js | 25 ++++- lib/binding_web/test/query-test.js | 109 +++++++++++++++++++- lib/binding_web/test/tree-test.js | 2 +- lib/binding_web/tree-sitter-web.d.ts | 57 +++++++++-- 11 files changed, 436 insertions(+), 35 deletions(-) diff --git a/lib/binding_web/binding.c b/lib/binding_web/binding.c index 78786094..b31d3a9b 100644 --- a/lib/binding_web/binding.c +++ b/lib/binding_web/binding.c @@ -121,6 +121,14 @@ extern void tree_sitter_log_callback( const char *message ); +extern bool tree_sitter_progress_callback( + uint32_t current_offset +); + +extern bool tree_sitter_query_progress_callback( + uint32_t current_offset +); + static const char *call_parse_callback( void *payload, uint32_t byte, @@ -150,6 +158,18 @@ static void call_log_callback( tree_sitter_log_callback(log_type == TSLogTypeLex, message); } +static bool progress_callback( + TSParseState *state +) { + return tree_sitter_progress_callback(state->current_byte_offset); +} + +static bool query_progress_callback( + TSQueryCursorState *state +) { + return tree_sitter_query_progress_callback(state->current_byte_offset); +} + void ts_parser_new_wasm() { TSParser *parser = ts_parser_new(); char *input_buffer = calloc(INPUT_BUFFER_SIZE, sizeof(char)); @@ -172,7 +192,8 @@ TSTree *ts_parser_parse_wasm( TSInput input = { input_buffer, call_parse_callback, - TSInputEncodingUTF16LE + TSInputEncodingUTF16LE, + NULL, }; if (range_count) { for (unsigned i = 0; i < range_count; i++) { @@ -183,7 +204,10 @@ TSTree *ts_parser_parse_wasm( } else { ts_parser_set_included_ranges(self, NULL, 0); } - return ts_parser_parse(self, old_tree, input); + + TSParseOptions options = {.payload = NULL, .progress_callback = progress_callback}; + + return ts_parser_parse_with_options(self, old_tree, input, options); } void ts_parser_included_ranges_wasm(TSParser *self) { @@ -278,6 +302,12 @@ void ts_tree_cursor_new_wasm(const TSTree *tree) { marshal_cursor(&cursor); } +void ts_tree_cursor_copy_wasm(const TSTree *tree) { + TSTreeCursor cursor = unmarshal_cursor(TRANSFER_BUFFER, tree); + TSTreeCursor copy = ts_tree_cursor_copy(&cursor); + marshal_cursor(©); +} + void ts_tree_cursor_delete_wasm(const TSTree *tree) { TSTreeCursor cursor = unmarshal_cursor(TRANSFER_BUFFER, tree); ts_tree_cursor_delete(&cursor); @@ -447,6 +477,11 @@ const char *ts_node_field_name_for_child_wasm(const TSTree *tree, uint32_t index return ts_node_field_name_for_child(node, index); } +const char *ts_node_field_name_for_named_child_wasm(const TSTree *tree, uint32_t index) { + TSNode node = unmarshal_node(tree); + return ts_node_field_name_for_named_child(node, index); +} + void ts_node_children_by_field_id_wasm(const TSTree *tree, uint32_t field_id) { TSNode node = unmarshal_node(tree); TSTreeCursor cursor = ts_tree_cursor_new(node); @@ -826,7 +861,10 @@ void ts_query_matches_wasm( ts_query_cursor_set_match_limit(scratch_query_cursor, match_limit); ts_query_cursor_set_max_start_depth(scratch_query_cursor, max_start_depth); ts_query_cursor_set_timeout_micros(scratch_query_cursor, timeout_micros); - ts_query_cursor_exec(scratch_query_cursor, self, node); + + TSQueryCursorOptions options = {.payload = NULL, .progress_callback = query_progress_callback}; + + ts_query_cursor_exec_with_options(scratch_query_cursor, self, node, &options); uint32_t index = 0; uint32_t match_count = 0; diff --git a/lib/binding_web/binding.js b/lib/binding_web/binding.js index a0305271..d5c33856 100644 --- a/lib/binding_web/binding.js +++ b/lib/binding_web/binding.js @@ -24,6 +24,10 @@ let MIN_COMPATIBLE_VERSION; let TRANSFER_BUFFER; let currentParseCallback; // eslint-disable-next-line no-unused-vars +let currentProgressCallback; +// eslint-disable-next-line no-unused-vars +let currentQueryProgressCallback; +// eslint-disable-next-line no-unused-vars let currentLogCallback; // eslint-disable-next-line no-unused-vars @@ -82,6 +86,12 @@ class ParserImpl { throw new Error('Argument must be a string or a function'); } + if (options?.progressCallback) { + currentProgressCallback = options.progressCallback; + } else { + currentProgressCallback = null; + } + if (this.logCallback) { currentLogCallback = this.logCallback; C._ts_parser_enable_logger_wasm(this[0], 1); @@ -113,12 +123,14 @@ class ParserImpl { if (!treeAddress) { currentParseCallback = null; currentLogCallback = null; + currentProgressCallback = null; throw new Error('Parsing failed'); } const result = new Tree(INTERNAL, treeAddress, this.language, currentParseCallback); currentParseCallback = null; currentLogCallback = null; + currentProgressCallback = null; return result; } @@ -326,7 +338,7 @@ class Node { } equals(other) { - return this.id === other.id; + return this.tree === other.tree && this.id === other.id; } child(index) { @@ -364,6 +376,20 @@ class Node { return result; } + fieldNameForNamedChild(index) { + marshalNode(this); + const address = C._ts_node_field_name_for_named_child_wasm( + this.tree[0], + index, + ); + if (!address) { + return null; + } + const result = AsciiToString(address); + // must not free, the string memory is owned by the language + return result; + } + childrenForFieldName(fieldName) { const fieldId = this.tree.language.fields.indexOf(fieldName); if (fieldId !== -1 && fieldId !== 0) return this.childrenForFieldId(fieldId); @@ -610,6 +636,35 @@ class Node { return new TreeCursor(INTERNAL, this.tree); } + edit(edit) { + if (this.startIndex >= edit.oldEndIndex) { + this.startIndex = edit.newEndIndex + (this.startIndex - edit.oldEndIndex); + let subbedPointRow; + let subbedPointColumn; + if (this.startPosition.row > edit.oldEndPosition.row) { + subbedPointRow = this.startPosition.row - edit.oldEndPosition.row; + subbedPointColumn = this.startPosition.column; + } else { + subbedPointRow = 0; + if (this.startPosition.column >= edit.oldEndPosition.column) { + subbedPointColumn = + this.startPosition.column - edit.oldEndPosition.column; + } + } + + if (subbedPointRow > 0) { + this.startPosition.row += subbedPointRow; + this.startPosition.column = subbedPointColumn; + } else { + this.startPosition.column += subbedPointColumn; + } + } else if (this.startIndex > edit.startIndex) { + this.startIndex = edit.newEndIndex; + this.startPosition.row = edit.newEndPosition.row; + this.startPosition.column = edit.newEndPosition.column; + } + } + toString() { marshalNode(this); const address = C._ts_node_to_string_wasm(this.tree[0]); @@ -626,6 +681,13 @@ class TreeCursor { unmarshalTreeCursor(this); } + copy() { + const copy = new TreeCursor(INTERNAL, this.tree); + C._ts_tree_cursor_copy_wasm(this.tree[0]); + unmarshalTreeCursor(copy); + return copy; + } + delete() { marshalTreeCursor(this); C._ts_tree_cursor_delete_wasm(this.tree[0]); @@ -808,6 +870,10 @@ class Language { } } + get name() { + return UTF8ToString(C._ts_language_name(this[0])); + } + get version() { return C._ts_language_version(this[0]); } @@ -951,6 +1017,7 @@ class Language { const captureCount = C._ts_query_capture_count(address); const patternCount = C._ts_query_pattern_count(address); const captureNames = new Array(captureCount); + const captureQuantifiers = new Array(patternCount); const stringValues = new Array(stringCount); for (let i = 0; i < captureCount; i++) { @@ -963,6 +1030,15 @@ class Language { captureNames[i] = UTF8ToString(nameAddress, nameLength); } + for (let i = 0; i < patternCount; i++) { + const captureQuantifiersArray = new Array(captureCount); + for (let j = 0; j < captureCount; j++) { + const quantifier = C._ts_query_capture_quantifier_for_id(address, i, j); + captureQuantifiersArray[j] = quantifier; + } + captureQuantifiers[i] = captureQuantifiersArray; + } + for (let i = 0; i < stringCount; i++) { const valueAddress = C._ts_query_string_value_for_id( address, @@ -1185,6 +1261,7 @@ class Language { INTERNAL, address, captureNames, + captureQuantifiers, textPredicates, predicates, Object.freeze(setProperties), @@ -1281,12 +1358,14 @@ class LookaheadIterable { class Query { constructor( - internal, address, captureNames, textPredicates, predicates, + internal, address, + captureNames, captureQuantifiers, textPredicates, predicates, setProperties, assertedProperties, refutedProperties, ) { assertInternal(internal); this[0] = address; this.captureNames = captureNames; + this.captureQuantifiers = captureQuantifiers; this.textPredicates = textPredicates; this.predicates = predicates; this.setProperties = setProperties; @@ -1310,6 +1389,7 @@ class Query { matchLimit = 0xFFFFFFFF, maxStartDepth = 0xFFFFFFFF, timeoutMicros = 0, + progressCallback, } = {}, ) { if (typeof matchLimit !== 'number') { @@ -1323,6 +1403,10 @@ class Query { throw new Error('`startPosition` cannot be greater than `endPosition`'); } + if (progressCallback) { + currentQueryProgressCallback = progressCallback; + } + marshalNode(node); C._ts_query_matches_wasm( @@ -1369,6 +1453,7 @@ class Query { result.length = filteredCount; C._free(startAddress); + currentQueryProgressCallback = null; return result; } @@ -1382,6 +1467,7 @@ class Query { matchLimit = 0xFFFFFFFF, maxStartDepth = 0xFFFFFFFF, timeoutMicros = 0, + progressCallback, } = {}, ) { if (typeof matchLimit !== 'number') { @@ -1395,6 +1481,10 @@ class Query { throw new Error('`startPosition` cannot be greater than `endPosition`'); } + if (progressCallback) { + currentQueryProgressCallback = progressCallback; + } + marshalNode(node); C._ts_query_captures_wasm( @@ -1443,6 +1533,7 @@ class Query { } C._free(startAddress); + currentQueryProgressCallback = null; return result; } @@ -1458,9 +1549,54 @@ class Query { C._free(captureNameAddress); } + disablePattern(patternIndex) { + if (patternIndex >= this.predicates.length) { + throw new Error( + `Pattern index is ${patternIndex} but the pattern count is ${this.predicates.length}`, + ); + } + C._ts_query_disable_pattern(this[0], patternIndex); + } + didExceedMatchLimit() { return this.exceededMatchLimit; } + + startIndexForPattern(patternIndex) { + if (patternIndex >= this.predicates.length) { + throw new Error( + `Pattern index is ${patternIndex} but the pattern count is ${this.predicates.length}`, + ); + } + return C._ts_query_start_byte_for_pattern(this[0], patternIndex); + } + + endIndexForPattern(patternIndex) { + if (patternIndex >= this.predicates.length) { + throw new Error( + `Pattern index is ${patternIndex} but the pattern count is ${this.predicates.length}`, + ); + } + return C._ts_query_end_byte_for_pattern(this[0], patternIndex); + } + + isPatternNonLocal(patternIndex) { + return C._ts_query_is_pattern_non_local(this[0], patternIndex) === 1; + } + + isPatternRooted(patternIndex) { + return C._ts_query_is_pattern_rooted(this[0], patternIndex) === 1; + } + + isPatternGuaranteedAtStep(patternIndex, stepIndex) { + return ( + C._ts_query_is_pattern_guaranteed_at_step( + this[0], + patternIndex, + stepIndex, + ) === 1 + ); + } } function getText(tree, startIndex, endIndex) { diff --git a/lib/binding_web/exports.txt b/lib/binding_web/exports.txt index 1b4d9e28..14da333d 100644 --- a/lib/binding_web/exports.txt +++ b/lib/binding_web/exports.txt @@ -10,9 +10,11 @@ "ts_language_symbol_for_name", "ts_language_symbol_name", "ts_language_symbol_type", +"ts_language_name", "ts_language_version", "ts_language_next_state", "ts_node_field_name_for_child_wasm", +"ts_node_field_name_for_named_child_wasm", "ts_node_children_by_field_id_wasm", "ts_node_first_child_for_byte_wasm", "ts_node_first_named_child_for_byte_wasm", @@ -68,8 +70,15 @@ "ts_query_pattern_count", "ts_query_predicates_for_pattern", "ts_query_disable_capture", +"ts_query_start_byte_for_pattern", +"ts_query_end_byte_for_pattern", "ts_query_string_count", "ts_query_string_value_for_id", +"ts_query_disable_pattern", +"ts_query_capture_quantifier_for_id", +"ts_query_is_pattern_non_local", +"ts_query_is_pattern_rooted", +"ts_query_is_pattern_guaranteed_at_step", "ts_tree_copy", "ts_tree_cursor_current_field_id_wasm", "ts_tree_cursor_current_depth_wasm", @@ -96,6 +105,7 @@ "ts_tree_cursor_reset_to_wasm", "ts_tree_cursor_start_index_wasm", "ts_tree_cursor_start_position_wasm", +"ts_tree_cursor_copy_wasm", "ts_tree_delete", "ts_tree_included_ranges_wasm", "ts_tree_edit_wasm", diff --git a/lib/binding_web/imports.js b/lib/binding_web/imports.js index dfb65bbe..d40fbfd4 100644 --- a/lib/binding_web/imports.js +++ b/lib/binding_web/imports.js @@ -22,4 +22,18 @@ mergeInto(LibraryManager.library, { currentLogCallback(message, isLexMessage !== 0); } }, + + tree_sitter_progress_callback(currentOffset) { + if (currentProgressCallback) { + return currentProgressCallback({currentOffset}); + } + return false; + }, + + tree_sitter_query_progress_callback(currentOffset) { + if (currentQueryProgressCallback) { + return currentQueryProgressCallback({currentOffset}); + } + return false; + }, }); diff --git a/lib/binding_web/test/helper.js b/lib/binding_web/test/helper.js index 1dec5a66..c70ed7f7 100644 --- a/lib/binding_web/test/helper.js +++ b/lib/binding_web/test/helper.js @@ -7,6 +7,7 @@ function languageURL(name) { module.exports = Parser.init().then(async () => ({ Parser, languageURL, + C: await Parser.Language.load(languageURL('c')), EmbeddedTemplate: await Parser.Language.load(languageURL('embedded-template')), HTML: await Parser.Language.load(languageURL('html')), JavaScript: await Parser.Language.load(languageURL('javascript')), diff --git a/lib/binding_web/test/language-test.js b/lib/binding_web/test/language-test.js index f804c8b5..e2f40e88 100644 --- a/lib/binding_web/test/language-test.js +++ b/lib/binding_web/test/language-test.js @@ -4,6 +4,13 @@ let JavaScript; describe('Language', () => { before(async () => ({JavaScript, Rust} = await require('./helper'))); + describe('.name, .version', () => { + it('returns the name and version of the language', () => { + assert.equal('javascript', JavaScript.name); + assert.equal(15, JavaScript.version); + }); + }); + describe('.fieldIdForName, .fieldNameForId', () => { it('converts between the string and integer representations of fields', () => { const nameId = JavaScript.fieldIdForName('name'); diff --git a/lib/binding_web/test/node-test.js b/lib/binding_web/test/node-test.js index bd432d90..3aa76383 100644 --- a/lib/binding_web/test/node-test.js +++ b/lib/binding_web/test/node-test.js @@ -1,5 +1,5 @@ const {assert} = require('chai'); -let Parser; let JavaScript; let JSON; let EmbeddedTemplate; let Python; +let Parser; let C; let JavaScript; let JSON; let EmbeddedTemplate; let Python; const JSON_EXAMPLE = ` @@ -35,7 +35,7 @@ describe('Node', () => { let parser; let tree; before(async () => - ({Parser, EmbeddedTemplate, JavaScript, JSON, Python} = await require('./helper')), + ({Parser, C, EmbeddedTemplate, JavaScript, JSON, Python} = await require('./helper')), ); beforeEach(() => { @@ -620,17 +620,57 @@ describe('Node', () => { describe('.fieldNameForChild(index)', () => { it('returns the field of a child or null', () => { - tree = parser.parse('let a = 5'); + parser.setLanguage(C); + tree = parser.parse('int w = x + /* y is special! */ y;'); - const noField = tree.rootNode.fieldNameForChild(0); - const name = tree.rootNode.firstChild.children[1].fieldNameForChild(0); - const value = tree.rootNode.firstChild.children[1].fieldNameForChild(2); - const overflow = tree.rootNode.firstChild.children[1].fieldNameForChild(3); + const translationUnitNode = tree.rootNode; + const declarationNode = translationUnitNode.firstChild; + const binaryExpressionNode = declarationNode + .childForFieldName('declarator') + .childForFieldName('value'); - assert.equal(noField, null); - assert.equal(name, 'name'); - assert.equal(value, 'value'); - assert.equal(overflow, null); + // ------------------- + // left: (identifier) 0 + // operator: "+" _ <--- (not a named child) + // (comment) 1 <--- (is an extra) + // right: (identifier) 2 + // ------------------- + + assert.equal(binaryExpressionNode.fieldNameForChild(0), 'left'); + assert.equal(binaryExpressionNode.fieldNameForChild(1), 'operator'); + // The comment should not have a field name, as it's just an extra + assert.equal(binaryExpressionNode.fieldNameForChild(2), null); + assert.equal(binaryExpressionNode.fieldNameForChild(3), 'right'); + // Negative test - Not a valid child index + assert.equal(binaryExpressionNode.fieldNameForChild(4), null); + }); + }); + + describe('.fieldNameForNamedChild(index)', () => { + it('returns the field of a named child or null', () => { + parser.setLanguage(C); + tree = parser.parse('int w = x + /* y is special! */ y;'); + + const translationUnitNode = tree.rootNode; + const declarationNode = translationUnitNode.firstNamedChild; + const binaryExpressionNode = declarationNode + .childForFieldName('declarator') + .childForFieldName('value'); + + // ------------------- + // left: (identifier) 0 + // operator: "+" _ <--- (not a named child) + // (comment) 1 <--- (is an extra) + // right: (identifier) 2 + // ------------------- + + assert.equal(binaryExpressionNode.fieldNameForNamedChild(0), 'left'); + // The comment should not have a field name, as it's just an extra + assert.equal(binaryExpressionNode.fieldNameForNamedChild(1), null); + // The operator is not a named child, so the named child at index 2 is the right child + assert.equal(binaryExpressionNode.fieldNameForNamedChild(2), 'right'); + // Negative test - Not a valid child index + assert.equal(binaryExpressionNode.fieldNameForNamedChild(3), null); }); }); }); diff --git a/lib/binding_web/test/parser-test.js b/lib/binding_web/test/parser-test.js index 4c58d020..37ddb939 100644 --- a/lib/binding_web/test/parser-test.js +++ b/lib/binding_web/test/parser-test.js @@ -1,11 +1,11 @@ const {assert} = require('chai'); -let Parser; let JavaScript; let HTML; let languageURL; +let Parser; let JavaScript; let HTML; let languageURL; let JSON; describe('Parser', () => { let parser; before(async () => - ({Parser, JavaScript, HTML, languageURL} = await require('./helper')), + ({Parser, JavaScript, HTML, JSON, languageURL} = await require('./helper')), ); beforeEach(() => { @@ -388,5 +388,26 @@ describe('Parser', () => { '(program (expression_statement (call_expression function: (identifier) arguments: (arguments))) (expression_statement (identifier)))', ); }); + + it('parses with a timeout', () => { + parser.setLanguage(JSON); + + const startTime = performance.now(); + assert.throws(() => { + parser.parse( + (offset, _) => offset === 0 ? '[' : ',0', + null, + { + progressCallback: (_) => { + if (performance.now() - startTime > 1) { + return true; + } + return false; + }, + }, + ); + }, + ); + }).timeout(5000); }); }); diff --git a/lib/binding_web/test/query-test.js b/lib/binding_web/test/query-test.js index db4c10f8..1a3e3a44 100644 --- a/lib/binding_web/test/query-test.js +++ b/lib/binding_web/test/query-test.js @@ -455,13 +455,112 @@ describe('Query', () => { describe('Set a timeout', () => it('returns less than the expected matches', () => { tree = parser.parse('function foo() while (true) { } }\n'.repeat(1000)); - query = JavaScript.query('(function_declaration name: (identifier) @function)'); - const matches = query.matches(tree.rootNode, { timeoutMicros: 1000 }); + query = JavaScript.query( + '(function_declaration name: (identifier) @function)', + ); + const matches = query.matches(tree.rootNode, {timeoutMicros: 1000}); assert.isBelow(matches.length, 1000); - const matches2 = query.matches(tree.rootNode, { timeoutMicros: 0 }); + const matches2 = query.matches(tree.rootNode, {timeoutMicros: 0}); assert.equal(matches2.length, 1000); - }) - ); + })); + + describe('Start and end indices for patterns', () => { + it('Returns the start and end indices for a pattern', () => { + const patterns1 = ` +"+" @operator +"-" @operator +"*" @operator +"=" @operator +"=>" @operator + `.trim(); + + const patterns2 = ` +(identifier) @a +(string) @b +`.trim(); + + const patterns3 = ` +((identifier) @b (#match? @b i)) +(function_declaration name: (identifier) @c) +(method_definition name: (property_identifier) @d) +`.trim(); + + const source = patterns1 + patterns2 + patterns3; + + const query = JavaScript.query(source); + + assert.equal(query.startIndexForPattern(0), 0); + assert.equal(query.endIndexForPattern(0), '"+" @operator\n'.length); + assert.equal(query.startIndexForPattern(5), patterns1.length); + assert.equal( + query.endIndexForPattern(5), + patterns1.length + '(identifier) @a\n'.length, + ); + assert.equal( + query.startIndexForPattern(7), + patterns1.length + patterns2.length, + ); + assert.equal( + query.endIndexForPattern(7), + patterns1.length + + patterns2.length + + '((identifier) @b (#match? @b i))\n'.length, + ); + }); + }); + + describe('Disable pattern', () => { + it('Disables patterns in the query', () => { + const query = JavaScript.query(` + (function_declaration name: (identifier) @name) + (function_declaration body: (statement_block) @body) + (class_declaration name: (identifier) @name) + (class_declaration body: (class_body) @body) + `); + + // disable the patterns that match names + query.disablePattern(0); + query.disablePattern(2); + + const source = 'class A { constructor() {} } function b() { return 1; }'; + tree = parser.parse(source); + const matches = query.matches(tree.rootNode); + assert.deepEqual(formatMatches(matches), [ + { + pattern: 3, + captures: [{name: 'body', text: '{ constructor() {} }'}], + }, + {pattern: 1, captures: [{name: 'body', text: '{ return 1; }'}]}, + ]); + }); + }); + + describe('Executes with a timeout', () => { + it('Returns less than the expected matches', () => { + tree = parser.parse('function foo() while (true) { } }\n'.repeat(1000)); + query = JavaScript.query( + '(function_declaration) @function', + ); + + const startTime = performance.now(); + + const matches = query.matches( + tree.rootNode, + { + progressCallback: (_) => { + if (performance.now() - startTime > 1) { + return true; + } + return false; + }, + }, + ); + assert.isBelow(matches.length, 1000); + + const matches2 = query.matches(tree.rootNode); + assert.equal(matches2.length, 1000); + }); + }); }); function formatMatches(matches) { diff --git a/lib/binding_web/test/tree-test.js b/lib/binding_web/test/tree-test.js index c9216eb1..29993eb6 100644 --- a/lib/binding_web/test/tree-test.js +++ b/lib/binding_web/test/tree-test.js @@ -363,7 +363,7 @@ describe('Tree', () => { ); const tree2 = tree.copy(); - ([input, edit] = spliceInput(input, 3, 0, '123')); + [input, edit] = spliceInput(input, 3, 0, '123'); assert.equal(input, 'abc123 + cde'); tree.edit(edit); diff --git a/lib/binding_web/tree-sitter-web.d.ts b/lib/binding_web/tree-sitter-web.d.ts index cfd8a102..18e17bbf 100644 --- a/lib/binding_web/tree-sitter-web.d.ts +++ b/lib/binding_web/tree-sitter-web.d.ts @@ -1,4 +1,4 @@ -declare module 'web-tree-sitter' { +declare module "web-tree-sitter" { class Parser { /** * @@ -6,7 +6,11 @@ declare module 'web-tree-sitter' { */ static init(moduleOptions?: object): Promise; delete(): void; - parse(input: string | Parser.Input, oldTree?: Parser.Tree, options?: Parser.Options): Parser.Tree; + parse( + input: string | Parser.Input, + oldTree?: Parser.Tree, + options?: Parser.Options, + ): Parser.Tree; getIncludedRanges(): Parser.Range[]; getTimeoutMicros(): number; setTimeoutMicros(timeout: number): void; @@ -20,6 +24,11 @@ declare module 'web-tree-sitter' { namespace Parser { export type Options = { includedRanges?: Range[]; + progressCallback?: (state: Parser.State) => boolean; + }; + + export type State = { + currentOffset: number; }; export type Point = { @@ -28,10 +37,10 @@ declare module 'web-tree-sitter' { }; export type Range = { - startIndex: number, - endIndex: number, - startPosition: Point, - endPosition: Point + startIndex: number; + endIndex: number; + startPosition: Point; + endPosition: Point; }; export type Edit = { @@ -46,7 +55,7 @@ declare module 'web-tree-sitter' { export type Logger = ( message: string, params: { [param: string]: string }, - type: "parse" | "lex" + type: "parse" | "lex", ) => void; export interface Input { @@ -105,10 +114,20 @@ declare module 'web-tree-sitter' { namedDescendantForIndex(index: number): SyntaxNode; namedDescendantForIndex(startIndex: number, endIndex: number): SyntaxNode; descendantForPosition(position: Point): SyntaxNode; - descendantForPosition(startPosition: Point, endPosition: Point): SyntaxNode; + descendantForPosition( + startPosition: Point, + endPosition: Point, + ): SyntaxNode; namedDescendantForPosition(position: Point): SyntaxNode; - namedDescendantForPosition(startPosition: Point, endPosition: Point): SyntaxNode; - descendantsOfType(types: String | Array, startPosition?: Point, endPosition?: Point): Array; + namedDescendantForPosition( + startPosition: Point, + endPosition: Point, + ): SyntaxNode; + descendantsOfType( + types: String | Array, + startPosition?: Point, + endPosition?: Point, + ): Array; walk(): TreeCursor; } @@ -131,6 +150,7 @@ declare module 'web-tree-sitter' { readonly currentDepth: number; readonly currentDescendantIndex: number; + copy(): TreeCursor; reset(node: SyntaxNode): void; resetTo(cursor: TreeCursor): void; delete(): void; @@ -171,6 +191,10 @@ declare module 'web-tree-sitter' { captures: QueryCapture[]; } + export type QueryState = { + currentOffset: number; + }; + export type QueryOptions = { startPosition?: Point; endPosition?: Point; @@ -179,6 +203,7 @@ declare module 'web-tree-sitter' { matchLimit?: number; maxStartDepth?: number; timeoutMicros?: number; + progressCallback: (state: QueryState) => boolean; }; export interface PredicateResult { @@ -186,8 +211,17 @@ declare module 'web-tree-sitter' { operands: { name: string; type: string }[]; } + export enum CaptureQuantifier { + Zero = 0, + ZeroOrOne = 1, + ZeroOrMore = 2, + One = 3, + OneOrMore = 4, + } + export class Query { captureNames: string[]; + captureQuantifiers: CaptureQuantifier[]; readonly predicates: { [name: string]: Function }[]; readonly setProperties: any[]; readonly assertedProperties: any[]; @@ -204,6 +238,7 @@ declare module 'web-tree-sitter' { isPatternRooted(patternIndex: number): boolean; isPatternNonLocal(patternIndex: number): boolean; startIndexForPattern(patternIndex: number): number; + endIndexForPattern(patternIndex: number): number; didExceedMatchLimit(): boolean; } @@ -238,5 +273,5 @@ declare module 'web-tree-sitter' { } } - export = Parser + export = Parser; }