feat(web): add missing API functions
Co-authored-by: Will Lillis <will.lillis24@gmail.com>
This commit is contained in:
parent
dcdd6ce2d2
commit
45fa028201
11 changed files with 436 additions and 35 deletions
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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')),
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
57
lib/binding_web/tree-sitter-web.d.ts
vendored
57
lib/binding_web/tree-sitter-web.d.ts
vendored
|
|
@ -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<void>;
|
||||
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<String>, startPosition?: Point, endPosition?: Point): Array<SyntaxNode>;
|
||||
namedDescendantForPosition(
|
||||
startPosition: Point,
|
||||
endPosition: Point,
|
||||
): SyntaxNode;
|
||||
descendantsOfType(
|
||||
types: String | Array<String>,
|
||||
startPosition?: Point,
|
||||
endPosition?: Point,
|
||||
): Array<SyntaxNode>;
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue