treesitter: add predicate "any-of?" (#14344)

For the case of Clojure and other Lisp syntax highlighting, it is
necessary to create huge regexps consisting of hundreds of symbols with
the pipe (|) character. To make things more difficult, these Lisp
symbols sometimes consists of special characters that are themselves
part of special regexp characters like '*'. In addition to being
difficult to maintain, it's performance is suboptimal.

This patch introduces a new predicate to perform 'source' matching in
amortized constant time. This is accomplished by compiling a hash table
on the first use.
This commit is contained in:
Raymond W. Ko 2021-05-14 11:41:20 -04:00 committed by GitHub
parent 61aefaf299
commit 7c95697026
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 84 additions and 2 deletions

View File

@ -211,6 +211,11 @@ Here is a list of built-in predicates :
text corresponding to the node : >
((identifier) @foo (#contains? @foo "foo"))
((identifier) @foo-bar (#contains @foo-bar "foo" "bar"))
<
`any-of?` *ts-predicate-any-of?*
Will check if the text is the same as any of the following
arguments : >
((identifier) @foo (#any-of? @foo "foo" "bar"))
<
*lua-treesitter-not-predicate*
Each predicate has a `not-` prefixed predicate that is just the negation of

View File

@ -260,7 +260,25 @@ local predicate_handlers = {
end
return false
end
end,
["any-of?"] = function(match, _, source, predicate)
local node = match[predicate[2]]
local node_text = M.get_node_text(node, source)
-- Since 'predicate' will not be used by callers of this function, use it
-- to store a string set built from the list of words to check against.
local string_set = predicate["string_set"]
if not string_set then
string_set = {}
for i=3,#predicate do
string_set[predicate[i]] = true
end
predicate["string_set"] = string_set
end
return string_set[node_text]
end,
}
-- As we provide lua-match? also expose vim-match?

View File

@ -270,6 +270,65 @@ void ui_refresh(void)
}, res)
end)
it('supports builtin query predicate any-of?', function()
if pending_c_parser(pending) then return end
insert([[
#include <stdio.h>
int main(void) {
int i;
for(i=1; i<=100; i++) {
if(((i%3)||(i%5))== 0)
printf("number= %d FizzBuzz\n", i);
else if((i%3)==0)
printf("number= %d Fizz\n", i);
else if((i%5)==0)
printf("number= %d Buzz\n", i);
else
printf("number= %d\n",i);
}
return 0;
}
]])
exec_lua([[
function get_query_result(query_text)
cquery = vim.treesitter.parse_query("c", query_text)
parser = vim.treesitter.get_parser(0, "c")
tree = parser:parse()[1]
res = {}
for cid, node in cquery:iter_captures(tree:root(), 0) do
-- can't transmit node over RPC. just check the name, range, and text
local text = vim.treesitter.get_node_text(node, 0)
local range = {node:range()}
table.insert(res, {cquery.captures[cid], node:type(), range, text})
end
return res
end
]])
local res0 = exec_lua([[return get_query_result(...)]],
[[((primitive_type) @c-keyword (#any-of? @c-keyword "int" "float"))]])
eq({
{ "c-keyword", "primitive_type", { 2, 2, 2, 5 }, "int" },
{ "c-keyword", "primitive_type", { 3, 4, 3, 7 }, "int" },
}, res0)
local res1 = exec_lua([[return get_query_result(...)]],
[[
((string_literal) @fizzbuzz-strings (#any-of? @fizzbuzz-strings
"\"number= %d FizzBuzz\\n\""
"\"number= %d Fizz\\n\""
"\"number= %d Buzz\\n\""
))
]])
eq({
{ "fizzbuzz-strings", "string_literal", { 6, 15, 6, 38 }, "\"number= %d FizzBuzz\\n\""},
{ "fizzbuzz-strings", "string_literal", { 8, 15, 8, 34 }, "\"number= %d Fizz\\n\""},
{ "fizzbuzz-strings", "string_literal", { 10, 15, 10, 34 }, "\"number= %d Buzz\\n\""},
}, res1)
end)
it('allow loading query with escaped quotes and capture them with `lua-match?` and `vim-match?`', function()
if pending_c_parser(pending) then return end
@ -343,7 +402,7 @@ void ui_refresh(void)
return list
]]
eq({ 'contains?', 'eq?', 'is-main?', 'lua-match?', 'match?', 'vim-match?' }, res_list)
eq({ 'any-of?', 'contains?', 'eq?', 'is-main?', 'lua-match?', 'match?', 'vim-match?' }, res_list)
end)