emacs.git - Emacs source repository

3 min read Original article ↗

Before the fix, if user uses the NODE-ONLY option, we don't keep the capture names in the results, then predicates won't work because they can't reference capture names. * src/treesit.c (query_capture_remove_capture_name): New function. (Ftreesit_query_capture): Use the new function to remove capture names AFTER running the predicate. * test/src/treesit-tests.el: (treesit-query-node-only-and-grouped): New test.

diff --git a/src/treesit.c b/src/treesit.c
index bec37067b50..c08691af3d8 100644
--- a/src/treesit.c
+++ b/src/treesit.c

@@ -3977,6 +3977,22 @@ treesit_initialize_query (Lisp_Object query, const TSLanguage *lang,

}

}

+/* Go over a list from START to END (until the element eq to END),

+ replace (capture-name . node) with just node. */

+static void query_capture_remove_capture_name (Lisp_Object start,

+ Lisp_Object end)

+{

+ Lisp_Object tail = start;

+ FOR_EACH_TAIL (tail)

+ {

+ Lisp_Object cell = CAR (tail);

+ CHECK_CONS (cell);

+ XSETCAR (tail, CDR (cell));

+

+ if (EQ (CDR (tail), end)) return;

+ }

+}

+

DEFUN ("treesit-query-capture",

Ftreesit_query_capture,

Streesit_query_capture, 2, 6, 0,

@@ -4122,18 +4138,12 @@ the query. */)

TSQueryCapture capture = captures[idx];

Lisp_Object captured_node = make_treesit_node (lisp_parser,

capture.node);

-

- Lisp_Object cap;

- if (NILP (node_only))

- {

- const char *capture_name

- = ts_query_capture_name_for_id (treesit_query, capture.index,

- &capture_name_len);

- cap = Fcons (intern_c_string_1 (capture_name, capture_name_len),

- captured_node);

- }

- else

- cap = captured_node;

+ const char *capture_name

+ = ts_query_capture_name_for_id (treesit_query, capture.index,

+ &capture_name_len);

+ Lisp_Object cap

+ = Fcons (intern_c_string_1 (capture_name, capture_name_len),

+ captured_node);

if (NILP (grouped))

result = Fcons (cap, result); /* Mode 1. */

@@ -4166,12 +4176,24 @@ the query. */)

if (!NILP (predicate_signal_data))

break;

- /* Mode 1: Predicates didn't pass, roll back. */

- if (!match && NILP (grouped))

- result = prev_result;

- /* Mode 2: Predicates pass, add this match group. */

+ /* Mode 1: Roll back if predicate didn't pass, don't roll back if

+ predicate passed. */

+ if (NILP (grouped))

+ {

+ if (!match)

+ result = prev_result;

+ else if (!NILP (node_only))

+ query_capture_remove_capture_name (result, prev_result);

+ }

+ /* Mode 2: Add this match group if predicate pass, don't add this

+ group if predicate didn't pass. */

if (match && !NILP (grouped))

- result = Fcons (Fnreverse (match_group), result);

+ {

+ match_group = Fnreverse (match_group);

+ if (!NILP (node_only))

+ query_capture_remove_capture_name (match_group, Qnil);

+ result = Fcons (match_group, result);

+ }

}

/* Final clean up. */

@@ -590,6 +590,38 @@ BODY is the test body."

(treesit-pattern-expand "a\nb\rc\td\0e\"f\1g\\h\fi")

"\"a\\nb\\rc\\td\\0e\\\"f\1g\\\\h\fi\"")))))

+(ert-deftest treesit-query-node-only-and-grouped ()

+ "Tests for query API."

+ (skip-unless (treesit-language-available-p 'json))

+ (with-temp-buffer

+ (let (parser root-node)

+ (progn

+ (insert "[1,2,{\"name\": \"Bob\"},3]")

+ (setq parser (treesit-parser-create 'json)))

+

+ ;; Test NODE-ONLY.

+ (let ((res (treesit-query-capture 'json '((number) @num) nil nil t)))

+ (should (equal (length res) 3))

+ ;; First element should be a node rather than 'num.

+ (should (treesit-node-p (nth 0 res))))

+

+ ;; Test GROUPED.

+ (let ((res (treesit-query-capture 'json '((number) @num) nil nil nil t)))

+ (should (equal (length res) 3))

+ ;; First element should be a match group.

+ (should (consp (nth 0 res)))

+ ;; First element of the match group should be a cons (num . <node>).

+ (should (consp (nth 0 (nth 0 res))))

+ (should (eq (car (nth 0 (nth 0 res))) 'num)))

+

+ ;; Test NODE-ONLY + GROUPED.

+ (let ((res (treesit-query-capture 'json '((number) @num) nil nil t t)))

+ (should (equal (length res) 3))

+ ;; First element should be a match group.

+ (should (consp (nth 0 res)))

+ ;; First element of the match group should be a node.

+ (should (treesit-node-p (nth 0 (nth 0 res))))))))

+

;;; Narrow

(ert-deftest treesit-narrow ()