tree-sitter/lib/binding_web/test/query-test.js

227 lines
6.8 KiB
JavaScript
Raw Normal View History

2020-05-11 15:22:05 -07:00
const { assert } = require("chai");
let Parser, JavaScript;
describe("Query", () => {
let parser, tree, query;
2020-05-11 15:22:05 -07:00
before(async () => ({ Parser, JavaScript } = await require("./helper")));
beforeEach(() => {
parser = new Parser().setLanguage(JavaScript);
});
afterEach(() => {
parser.delete();
if (tree) tree.delete();
if (query) query.delete();
});
2020-05-11 15:22:05 -07:00
describe("construction", () => {
it("throws an error on invalid patterns", () => {
assert.throws(() => {
2020-05-11 15:22:05 -07:00
JavaScript.query("(function_declaration wat)");
}, "Bad syntax at offset 22: 'wat)'...");
assert.throws(() => {
2020-05-11 15:22:05 -07:00
JavaScript.query("(non_existent)");
}, "Bad node name 'non_existent'");
assert.throws(() => {
2020-05-11 15:22:05 -07:00
JavaScript.query("(a)");
}, "Bad node name 'a'");
assert.throws(() => {
2020-05-11 15:22:05 -07:00
JavaScript.query("(function_declaration non_existent:(identifier))");
}, "Bad field name 'non_existent'");
});
2019-09-16 10:25:44 -07:00
2020-05-11 15:22:05 -07:00
it("throws an error on invalid predicates", () => {
2019-09-16 10:25:44 -07:00
assert.throws(() => {
2020-05-11 15:22:05 -07:00
JavaScript.query("((identifier) @abc (#eq? @ab hi))");
2019-09-16 10:25:44 -07:00
}, "Bad capture name @ab");
assert.throws(() => {
2020-05-11 15:22:05 -07:00
JavaScript.query("((identifier) @abc (#eq? @ab hi))");
2019-09-16 10:25:44 -07:00
}, "Bad capture name @ab");
assert.throws(() => {
2020-05-11 15:22:05 -07:00
JavaScript.query("((identifier) @abc (#eq?))");
}, "Wrong number of arguments to `#eq?` predicate. Expected 2, got 0");
2019-09-16 10:25:44 -07:00
assert.throws(() => {
2020-05-11 15:22:05 -07:00
JavaScript.query("((identifier) @a (eq? @a @a @a))");
}, "Wrong number of arguments to `#eq?` predicate. Expected 2, got 3");
2019-09-16 10:25:44 -07:00
assert.throws(() => {
2020-05-11 15:22:05 -07:00
JavaScript.query("((identifier) @a (#something-else? @a))");
}, "Unknown query predicate `#something-else?`");
2019-09-16 10:25:44 -07:00
});
});
2020-05-11 15:22:05 -07:00
describe(".matches", () => {
it("returns all of the matches for the given query", () => {
tree = parser.parse("function one() { two(); function three() {} }");
query = JavaScript.query(`
2020-05-11 15:22:05 -07:00
(function_declaration name: (identifier) @fn-def)
(call_expression function: (identifier) @fn-ref)
`);
const matches = query.matches(tree.rootNode);
2020-05-11 15:22:05 -07:00
assert.deepEqual(formatMatches(matches), [
{ pattern: 0, captures: [{ name: "fn-def", text: "one" }] },
{ pattern: 1, captures: [{ name: "fn-ref", text: "two" }] },
{ pattern: 0, captures: [{ name: "fn-def", text: "three" }] },
]);
});
2020-05-11 15:22:05 -07:00
it("can search in a specified ranges", () => {
tree = parser.parse("[a, b,\nc, d,\ne, f,\ng, h]");
2020-05-11 15:22:05 -07:00
query = JavaScript.query("(identifier) @element");
const matches = query.matches(
tree.rootNode,
2020-05-11 15:22:05 -07:00
{ row: 1, column: 1 },
{ row: 3, column: 1 }
);
2020-05-11 15:22:05 -07:00
assert.deepEqual(formatMatches(matches), [
{ pattern: 0, captures: [{ name: "element", text: "d" }] },
{ pattern: 0, captures: [{ name: "element", text: "e" }] },
{ pattern: 0, captures: [{ name: "element", text: "f" }] },
{ pattern: 0, captures: [{ name: "element", text: "g" }] },
]);
});
});
2020-05-11 15:22:05 -07:00
describe(".captures", () => {
it("returns all of the captures for the given query, in order", () => {
tree = parser.parse(`
a({
bc: function de() {
const fg = function hi() {}
},
jk: function lm() {
const no = function pq() {}
},
});
`);
query = JavaScript.query(`
(pair
2020-05-11 15:22:05 -07:00
key: _ @method.def
(function
name: (identifier) @method.alias))
(variable_declarator
2020-05-11 15:22:05 -07:00
name: _ @function.def
value: (function
name: (identifier) @function.alias))
":" @delimiter
"=" @operator
`);
const captures = query.captures(tree.rootNode);
2020-05-11 15:22:05 -07:00
assert.deepEqual(formatCaptures(captures), [
{ name: "method.def", text: "bc" },
{ name: "delimiter", text: ":" },
{ name: "method.alias", text: "de" },
{ name: "function.def", text: "fg" },
{ name: "operator", text: "=" },
{ name: "function.alias", text: "hi" },
{ name: "method.def", text: "jk" },
{ name: "delimiter", text: ":" },
{ name: "method.alias", text: "lm" },
{ name: "function.def", text: "no" },
{ name: "operator", text: "=" },
{ name: "function.alias", text: "pq" },
]);
});
2019-09-16 10:25:44 -07:00
2020-05-11 15:22:05 -07:00
it("handles conditions that compare the text of capture to literal strings", () => {
2019-09-16 10:25:44 -07:00
tree = parser.parse(`
const ab = require('./ab');
new Cd(EF);
`);
query = JavaScript.query(`
(identifier) @variable
((identifier) @function.builtin
2020-05-11 15:22:05 -07:00
(#eq? @function.builtin "require"))
2019-09-16 10:25:44 -07:00
((identifier) @constructor
2020-05-11 15:22:05 -07:00
(#match? @constructor "^[A-Z]"))
2019-09-16 10:25:44 -07:00
((identifier) @constant
2020-05-11 15:22:05 -07:00
(#match? @constant "^[A-Z]{2,}$"))
2019-09-16 10:25:44 -07:00
`);
const captures = query.captures(tree.rootNode);
2020-05-11 15:22:05 -07:00
assert.deepEqual(formatCaptures(captures), [
{ name: "variable", text: "ab" },
{ name: "variable", text: "require" },
{ name: "function.builtin", text: "require" },
{ name: "variable", text: "Cd" },
{ name: "constructor", text: "Cd" },
{ name: "variable", text: "EF" },
{ name: "constructor", text: "EF" },
{ name: "constant", text: "EF" },
]);
2019-09-16 10:25:44 -07:00
});
2020-05-11 15:22:05 -07:00
it("handles conditions that compare the text of capture to each other", () => {
2019-09-16 10:25:44 -07:00
tree = parser.parse(`
ab = abc + 1;
def = de + 1;
ghi = ghi + 1;
2019-09-16 10:25:44 -07:00
`);
query = JavaScript.query(`
2020-05-11 15:22:05 -07:00
(
(assignment_expression
left: (identifier) @id1
right: (binary_expression
2019-09-16 10:25:44 -07:00
left: (identifier) @id2))
2020-05-11 15:22:05 -07:00
(#eq? @id1 @id2)
)
2019-09-16 10:25:44 -07:00
`);
const captures = query.captures(tree.rootNode);
2020-05-11 15:22:05 -07:00
assert.deepEqual(formatCaptures(captures), [
{ name: "id1", text: "ghi" },
{ name: "id2", text: "ghi" },
]);
2019-09-16 10:25:44 -07:00
});
2020-05-11 15:22:05 -07:00
it("handles patterns with properties", () => {
tree = parser.parse(`a(b.c);`);
query = JavaScript.query(`
((call_expression (identifier) @func)
2020-05-11 15:22:05 -07:00
(#set! foo)
(#set! bar baz))
((property_identifier) @prop
2020-05-11 15:22:05 -07:00
(#is? foo)
(#is-not? bar baz))
`);
const captures = query.captures(tree.rootNode);
assert.deepEqual(formatCaptures(captures), [
2020-05-11 15:22:05 -07:00
{ name: "func", text: "a", setProperties: { foo: null, bar: "baz" } },
{
name: "prop",
text: "c",
assertedProperties: { foo: null },
refutedProperties: { bar: "baz" },
},
]);
});
});
});
function formatMatches(matches) {
2020-05-11 15:22:05 -07:00
return matches.map(({ pattern, captures }) => ({
pattern,
2020-05-11 15:22:05 -07:00
captures: formatCaptures(captures),
}));
}
function formatCaptures(captures) {
2020-05-11 15:22:05 -07:00
return captures.map((c) => {
const node = c.node;
delete c.node;
c.text = node.text;
return c;
2020-05-11 15:22:05 -07:00
});
}