Merge pull request #722 from tree-sitter/query-last-child-fix
Fix behavior of the last child operator in tree queries
This commit is contained in:
commit
85e4636c80
4 changed files with 121 additions and 43 deletions
|
|
@ -667,6 +667,41 @@ fn test_query_matches_with_immediate_siblings() {
|
|||
(2, vec![("first-element", "1")]),
|
||||
],
|
||||
);
|
||||
|
||||
let query = Query::new(
|
||||
language,
|
||||
"
|
||||
(block . (_) @first-stmt)
|
||||
(block (_) @stmt)
|
||||
(block (_) @last-stmt .)
|
||||
",
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_query_matches(
|
||||
language,
|
||||
&query,
|
||||
"
|
||||
if a:
|
||||
b()
|
||||
c()
|
||||
if d(): e(); f()
|
||||
g()
|
||||
",
|
||||
&[
|
||||
(0, vec![("first-stmt", "b()")]),
|
||||
(1, vec![("stmt", "b()")]),
|
||||
(1, vec![("stmt", "c()")]),
|
||||
(1, vec![("stmt", "if d(): e(); f()")]),
|
||||
(0, vec![("first-stmt", "e()")]),
|
||||
(1, vec![("stmt", "e()")]),
|
||||
(1, vec![("stmt", "f()")]),
|
||||
(2, vec![("last-stmt", "f()")]),
|
||||
(1, vec![("stmt", "g()")]),
|
||||
(2, vec![("last-stmt", "g()")]),
|
||||
],
|
||||
);
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2549,11 +2549,13 @@ static inline bool ts_query_cursor__advance(
|
|||
if (symbol != ts_builtin_sym_error && self->query->symbol_map) {
|
||||
symbol = self->query->symbol_map[symbol];
|
||||
}
|
||||
bool can_have_later_siblings;
|
||||
bool has_later_siblings;
|
||||
bool has_later_named_siblings;
|
||||
bool can_have_later_siblings_with_this_field;
|
||||
TSFieldId field_id = ts_tree_cursor_current_status(
|
||||
&self->cursor,
|
||||
&can_have_later_siblings,
|
||||
&has_later_siblings,
|
||||
&has_later_named_siblings,
|
||||
&can_have_later_siblings_with_this_field
|
||||
);
|
||||
LOG(
|
||||
|
|
@ -2613,11 +2615,11 @@ static inline bool ts_query_cursor__advance(
|
|||
step->symbol == symbol ||
|
||||
step->symbol == WILDCARD_SYMBOL ||
|
||||
(step->symbol == NAMED_WILDCARD_SYMBOL && is_named);
|
||||
bool later_sibling_can_match = can_have_later_siblings;
|
||||
bool later_sibling_can_match = has_later_siblings;
|
||||
if ((step->is_immediate && is_named) || state->seeking_immediate_match) {
|
||||
later_sibling_can_match = false;
|
||||
}
|
||||
if (step->is_last_child && can_have_later_siblings) {
|
||||
if (step->is_last_child && has_later_named_siblings) {
|
||||
node_does_match = false;
|
||||
}
|
||||
if (step->field) {
|
||||
|
|
|
|||
|
|
@ -244,14 +244,18 @@ TSNode ts_tree_cursor_current_node(const TSTreeCursor *_self) {
|
|||
);
|
||||
}
|
||||
|
||||
// Private - Get various facts about the current node that are needed
|
||||
// when executing tree queries.
|
||||
TSFieldId ts_tree_cursor_current_status(
|
||||
const TSTreeCursor *_self,
|
||||
bool *can_have_later_siblings,
|
||||
bool *has_later_siblings,
|
||||
bool *has_later_named_siblings,
|
||||
bool *can_have_later_siblings_with_this_field
|
||||
) {
|
||||
const TreeCursor *self = (const TreeCursor *)_self;
|
||||
TSFieldId result = 0;
|
||||
*can_have_later_siblings = false;
|
||||
*has_later_siblings = false;
|
||||
*has_later_named_siblings = false;
|
||||
*can_have_later_siblings_with_this_field = false;
|
||||
|
||||
// Walk up the tree, visiting the current node and its invisible ancestors,
|
||||
|
|
@ -260,49 +264,86 @@ TSFieldId ts_tree_cursor_current_status(
|
|||
TreeCursorEntry *entry = &self->stack.contents[i];
|
||||
TreeCursorEntry *parent_entry = &self->stack.contents[i - 1];
|
||||
|
||||
// Stop walking up when a visible ancestor is found.
|
||||
if (i != self->stack.size - 1) {
|
||||
if (ts_subtree_visible(*entry->subtree)) break;
|
||||
if (
|
||||
!ts_subtree_extra(*entry->subtree) &&
|
||||
ts_language_alias_at(
|
||||
self->tree->language,
|
||||
parent_entry->subtree->ptr->production_id,
|
||||
entry->structural_child_index
|
||||
)
|
||||
) break;
|
||||
}
|
||||
|
||||
if (ts_subtree_child_count(*parent_entry->subtree) > entry->child_index + 1) {
|
||||
*can_have_later_siblings = true;
|
||||
}
|
||||
|
||||
if (ts_subtree_extra(*entry->subtree)) break;
|
||||
|
||||
const TSFieldMapEntry *field_map, *field_map_end;
|
||||
ts_language_field_map(
|
||||
const TSSymbol *alias_sequence = ts_language_alias_sequence(
|
||||
self->tree->language,
|
||||
parent_entry->subtree->ptr->production_id,
|
||||
&field_map, &field_map_end
|
||||
parent_entry->subtree->ptr->production_id
|
||||
);
|
||||
|
||||
// Look for a field name associated with the current node.
|
||||
if (!result) {
|
||||
for (const TSFieldMapEntry *i = field_map; i < field_map_end; i++) {
|
||||
if (!i->inherited && i->child_index == entry->structural_child_index) {
|
||||
result = i->field_id;
|
||||
*can_have_later_siblings_with_this_field = false;
|
||||
break;
|
||||
// If the subtree is visible, return its public-facing symbol.
|
||||
// Otherwise, return zero.
|
||||
#define subtree_visible_symbol(subtree, structural_child_index) \
|
||||
(( \
|
||||
!ts_subtree_extra(subtree) && \
|
||||
alias_sequence && \
|
||||
alias_sequence[structural_child_index] \
|
||||
) ? \
|
||||
alias_sequence[structural_child_index] : \
|
||||
ts_subtree_visible(subtree) ? \
|
||||
ts_subtree_symbol(subtree) : \
|
||||
0) \
|
||||
|
||||
// Stop walking up when a visible ancestor is found.
|
||||
if (
|
||||
i != self->stack.size - 1 &&
|
||||
subtree_visible_symbol(*entry->subtree, entry->structural_child_index)
|
||||
) break;
|
||||
|
||||
// Determine if the current node has later siblings.
|
||||
if (!*has_later_siblings) {
|
||||
unsigned sibling_count = parent_entry->subtree->ptr->child_count;
|
||||
unsigned structural_child_index = entry->structural_child_index;
|
||||
if (!ts_subtree_extra(*entry->subtree)) structural_child_index++;
|
||||
for (unsigned j = entry->child_index + 1; j < sibling_count; j++) {
|
||||
Subtree sibling = parent_entry->subtree->ptr->children[j];
|
||||
if (ts_subtree_visible_child_count(sibling) > 0) {
|
||||
*has_later_siblings = true;
|
||||
if (*has_later_named_siblings) break;
|
||||
if (sibling.ptr->named_child_count > 0) {
|
||||
*has_later_named_siblings = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
TSSymbol visible_symbol = subtree_visible_symbol(sibling, structural_child_index);
|
||||
if (visible_symbol) {
|
||||
*has_later_siblings = true;
|
||||
if (*has_later_named_siblings) break;
|
||||
if (ts_language_symbol_metadata(self->tree->language, visible_symbol).named) {
|
||||
*has_later_named_siblings = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!ts_subtree_extra(sibling)) structural_child_index++;
|
||||
}
|
||||
}
|
||||
|
||||
// Determine if there other later siblings with the same field name.
|
||||
if (result) {
|
||||
for (const TSFieldMapEntry *i = field_map; i < field_map_end; i++) {
|
||||
if (i->field_id == result && i->child_index > entry->structural_child_index) {
|
||||
*can_have_later_siblings_with_this_field = true;
|
||||
break;
|
||||
#undef subtree_visible_symbol
|
||||
|
||||
if (!ts_subtree_extra(*entry->subtree)) {
|
||||
const TSFieldMapEntry *field_map, *field_map_end;
|
||||
ts_language_field_map(
|
||||
self->tree->language,
|
||||
parent_entry->subtree->ptr->production_id,
|
||||
&field_map, &field_map_end
|
||||
);
|
||||
|
||||
// Look for a field name associated with the current node.
|
||||
if (!result) {
|
||||
for (const TSFieldMapEntry *i = field_map; i < field_map_end; i++) {
|
||||
if (!i->inherited && i->child_index == entry->structural_child_index) {
|
||||
result = i->field_id;
|
||||
*can_have_later_siblings_with_this_field = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Determine if the current node can have later siblings with the same field name.
|
||||
if (result) {
|
||||
for (const TSFieldMapEntry *i = field_map; i < field_map_end; i++) {
|
||||
if (i->field_id == result && i->child_index > entry->structural_child_index) {
|
||||
*can_have_later_siblings_with_this_field = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,6 @@ typedef struct {
|
|||
} TreeCursor;
|
||||
|
||||
void ts_tree_cursor_init(TreeCursor *, TSNode);
|
||||
TSFieldId ts_tree_cursor_current_status(const TSTreeCursor *, bool *, bool *);
|
||||
TSFieldId ts_tree_cursor_current_status(const TSTreeCursor *, bool *, bool *, bool *);
|
||||
|
||||
#endif // TREE_SITTER_TREE_CURSOR_H_
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue