Add containing range APIs to query cursor

Co-authored-by: Kirill Bulatov <mail4score@gmail.com>
Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
Co-authored-by: dino <dinojoaocosta@gmail.com>
Co-authored-by: John Tur <john-tur@outlook.com>
Co-authored-by: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com>
Co-authored-by: dino <dinojoaocosta@gmail.com>
Co-authored-by: Will Lillis <will.lillis24@gmail.com>
This commit is contained in:
Piotr Osiewicz 2025-10-02 19:02:05 +02:00 committed by Will Lillis
parent 7d3feeae9a
commit c0b1710f8a
13 changed files with 420 additions and 34 deletions

View file

@ -874,6 +874,12 @@ void ts_query_matches_wasm(
uint32_t end_column,
uint32_t start_index,
uint32_t end_index,
uint32_t start_containing_row,
uint32_t start_containing_column,
uint32_t end_containing_row,
uint32_t end_containing_column,
uint32_t start_containing_index,
uint32_t end_containing_index,
uint32_t match_limit,
uint32_t max_start_depth
) {
@ -889,8 +895,20 @@ void ts_query_matches_wasm(
TSNode node = unmarshal_node(tree);
TSPoint start_point = {start_row, code_unit_to_byte(start_column)};
TSPoint end_point = {end_row, code_unit_to_byte(end_column)};
TSPoint start_containing_point = {start_containing_row, code_unit_to_byte(start_containing_column)};
TSPoint end_containing_point = {end_containing_row, code_unit_to_byte(end_containing_column)};
ts_query_cursor_set_point_range(scratch_query_cursor, start_point, end_point);
ts_query_cursor_set_byte_range(scratch_query_cursor, start_index, end_index);
ts_query_cursor_set_containing_point_range(
scratch_query_cursor,
start_containing_point,
end_containing_point
);
ts_query_cursor_set_containing_byte_range(
scratch_query_cursor,
start_containing_index,
end_containing_index
);
ts_query_cursor_set_match_limit(scratch_query_cursor, match_limit);
ts_query_cursor_set_max_start_depth(scratch_query_cursor, max_start_depth);
@ -932,6 +950,12 @@ void ts_query_captures_wasm(
uint32_t end_column,
uint32_t start_index,
uint32_t end_index,
uint32_t start_containing_row,
uint32_t start_containing_column,
uint32_t end_containing_row,
uint32_t end_containing_column,
uint32_t start_containing_index,
uint32_t end_containing_index,
uint32_t match_limit,
uint32_t max_start_depth
) {
@ -944,8 +968,20 @@ void ts_query_captures_wasm(
TSNode node = unmarshal_node(tree);
TSPoint start_point = {start_row, code_unit_to_byte(start_column)};
TSPoint end_point = {end_row, code_unit_to_byte(end_column)};
TSPoint start_containing_point = {start_containing_row, code_unit_to_byte(start_containing_column)};
TSPoint end_containing_point = {end_containing_row, code_unit_to_byte(end_containing_column)};
ts_query_cursor_set_point_range(scratch_query_cursor, start_point, end_point);
ts_query_cursor_set_byte_range(scratch_query_cursor, start_index, end_index);
ts_query_cursor_set_containing_point_range(
scratch_query_cursor,
start_containing_point,
end_containing_point
);
ts_query_cursor_set_containing_byte_range(
scratch_query_cursor,
start_containing_index,
end_containing_index
);
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_exec(scratch_query_cursor, self, node);

View file

@ -175,8 +175,8 @@ interface WasmModule {
_ts_node_is_extra_wasm(_0: number): number;
_ts_node_parse_state_wasm(_0: number): number;
_ts_node_next_parse_state_wasm(_0: number): number;
_ts_query_matches_wasm(_0: number, _1: number, _2: number, _3: number, _4: number, _5: number, _6: number, _7: number, _8: number, _9: number): void;
_ts_query_captures_wasm(_0: number, _1: number, _2: number, _3: number, _4: number, _5: number, _6: number, _7: number, _8: number, _9: number): void;
_ts_query_matches_wasm(_0: number, _1: number, _2: number, _3: number, _4: number, _5: number, _6: number, _7: number, _8: number, _9: number, _10: number, _11: number, _12: number, _13: number, _14: number, _15: number): void;
_ts_query_captures_wasm(_0: number, _1: number, _2: number, _3: number, _4: number, _5: number, _6: number, _7: number, _8: number, _9: number, _10: number, _11: number, _12: number, _13: number, _14: number, _15: number): void;
_memset(_0: number, _1: number, _2: number): number;
_memcpy(_0: number, _1: number, _2: number): number;
_memmove(_0: number, _1: number, _2: number): number;

View file

@ -20,12 +20,32 @@ export interface QueryOptions {
/** The end position of the range to query */
endPosition?: Point;
/** The start position of the range to query Only the matches that are fully
* contained within provided range will be returned.
**/
startContainingPosition?: Point;
/** The end position of the range to query Only the matches that are fully
* contained within provided range will be returned.
**/
endContainingPosition?: Point;
/** The start index of the range to query */
startIndex?: number;
/** The end index of the range to query */
endIndex?: number;
/** The start index of the range to query Only the matches that are fully
* contained within provided range will be returned.
**/
startContainingIndex?: number;
/** The end index of the range to query Only the matches that are fully
* contained within provided range will be returned.
**/
endContainingIndex?: number;
/**
* The maximum number of in-progress matches for this query.
* The limit must be > 0 and <= 65536.
@ -695,6 +715,10 @@ export class Query {
const endPosition = options.endPosition ?? ZERO_POINT;
const startIndex = options.startIndex ?? 0;
const endIndex = options.endIndex ?? 0;
const startContainingPosition = options.startContainingPosition ?? ZERO_POINT;
const endContainingPosition = options.endContainingPosition ?? ZERO_POINT;
const startContainingIndex = options.startContainingIndex ?? 0;
const endContainingIndex = options.endContainingIndex ?? 0;
const matchLimit = options.matchLimit ?? 0xFFFFFFFF;
const maxStartDepth = options.maxStartDepth ?? 0xFFFFFFFF;
const progressCallback = options.progressCallback;
@ -715,6 +739,18 @@ export class Query {
throw new Error('`startPosition` cannot be greater than `endPosition`');
}
if (endContainingIndex !== 0 && startContainingIndex > endContainingIndex) {
throw new Error('`startContainingIndex` cannot be greater than `endContainingIndex`');
}
if (endContainingPosition !== ZERO_POINT && (
startContainingPosition.row > endContainingPosition.row ||
(startContainingPosition.row === endContainingPosition.row &&
startContainingPosition.column > endContainingPosition.column)
)) {
throw new Error('`startContainingPosition` cannot be greater than `endContainingPosition`');
}
if (progressCallback) {
C.currentQueryProgressCallback = progressCallback;
}
@ -730,6 +766,12 @@ export class Query {
endPosition.column,
startIndex,
endIndex,
startContainingPosition.row,
startContainingPosition.column,
endContainingPosition.row,
endContainingPosition.column,
startContainingIndex,
endContainingIndex,
matchLimit,
maxStartDepth,
);
@ -788,6 +830,10 @@ export class Query {
const endPosition = options.endPosition ?? ZERO_POINT;
const startIndex = options.startIndex ?? 0;
const endIndex = options.endIndex ?? 0;
const startContainingPosition = options.startContainingPosition ?? ZERO_POINT;
const endContainingPosition = options.endContainingPosition ?? ZERO_POINT;
const startContainingIndex = options.startContainingIndex ?? 0;
const endContainingIndex = options.endContainingIndex ?? 0;
const matchLimit = options.matchLimit ?? 0xFFFFFFFF;
const maxStartDepth = options.maxStartDepth ?? 0xFFFFFFFF;
const progressCallback = options.progressCallback;
@ -808,6 +854,18 @@ export class Query {
throw new Error('`startPosition` cannot be greater than `endPosition`');
}
if (endContainingIndex !== 0 && startContainingIndex > endContainingIndex) {
throw new Error('`startContainingIndex` cannot be greater than `endContainingIndex`');
}
if (endContainingPosition !== ZERO_POINT && (
startContainingPosition.row > endContainingPosition.row ||
(startContainingPosition.row === endContainingPosition.row &&
startContainingPosition.column > endContainingPosition.column)
)) {
throw new Error('`startContainingPosition` cannot be greater than `endContainingPosition`');
}
if (progressCallback) {
C.currentQueryProgressCallback = progressCallback;
}
@ -823,6 +881,12 @@ export class Query {
endPosition.column,
startIndex,
endIndex,
startContainingPosition.row,
startContainingPosition.column,
endContainingPosition.row,
endContainingPosition.column,
startContainingIndex,
endContainingIndex,
matchLimit,
maxStartDepth,
);

View file

@ -96,6 +96,64 @@ describe('Query', () => {
]);
});
it('can search in contained within point ranges', () => {
tree = parser.parse(`[
{"key1": "value1"},
{"key2": "value2"},
{"key3": "value3"},
{"key4": "value4"},
{"key5": "value5"},
{"key6": "value6"},
{"key7": "value7"},
{"key8": "value8"},
{"key9": "value9"},
{"key10": "value10"},
{"key11": "value11"},
{"key12": "value12"},
]`)!;
query = new Query(JavaScript, '("[" @l_bracket "]" @r_bracket) ("{" @l_brace "}" @r_brace)');
const matches = query.matches(
tree.rootNode,
{
startContainingPosition: { row: 5, column: 0 },
endContainingPosition: { row: 7, column: 0 },
}
);
expect(formatMatches(matches)).toEqual([
{ patternIndex: 1, captures: [{ patternIndex: 1, name: 'l_brace', text: '{' }, { patternIndex: 1, name: 'r_brace', text: '}' },] },
{ patternIndex: 1, captures: [{ patternIndex: 1, name: 'l_brace', text: '{' }, { patternIndex: 1, name: 'r_brace', text: '}' },] },
]);
});
it('can search in contained within byte ranges', () => {
tree = parser.parse(`[
{"key1": "value1"},
{"key2": "value2"},
{"key3": "value3"},
{"key4": "value4"},
{"key5": "value5"},
{"key6": "value6"},
{"key7": "value7"},
{"key8": "value8"},
{"key9": "value9"},
{"key10": "value10"},
{"key11": "value11"},
{"key12": "value12"},
]`)!;
query = new Query(JavaScript, '("[" @l_bracket "]" @r_bracket) ("{" @l_brace "}" @r_brace)');
const matches = query.matches(
tree.rootNode,
{
startContainingIndex: 290,
endContainingIndex: 432,
}
);
expect(formatMatches(matches)).toEqual([
{ patternIndex: 1, captures: [{ patternIndex: 1, name: 'l_brace', text: '{' }, { patternIndex: 1, name: 'r_brace', text: '}' },] },
{ patternIndex: 1, captures: [{ patternIndex: 1, name: 'l_brace', text: '{' }, { patternIndex: 1, name: 'r_brace', text: '}' },] },
]);
});
it('handles predicates that compare the text of capture to literal strings', () => {
tree = parser.parse(`
giraffe(1, 2, []);