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. */ diff --git a/test/src/treesit-tests.el b/test/src/treesit-tests.el index 5492a45b1de..58579e82f7d 100644 --- a/test/src/treesit-tests.el +++ b/test/src/treesit-tests.el @@ -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 . ). + (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 ()