]> git.eshelyaron.com Git - emacs.git/commitdiff
Correct conversion of strings to tree-sitter query syntax
authorMattias Engdegård <mattiase@acm.org>
Fri, 16 Jun 2023 10:37:07 +0000 (12:37 +0200)
committerMattias Engdegård <mattiase@acm.org>
Fri, 16 Jun 2023 10:58:57 +0000 (12:58 +0200)
The treesitter query syntax for string literals differs from that of
Elisp so we cannot just use the Lisp printer (bug#64017).

* src/treesit.c (treesit_query_string_string): New function.
(Ftreesit_pattern_expand): Use it.
* test/src/treesit-tests.el (treesit-query-api): Add test case.

src/treesit.c
test/src/treesit-tests.el

index 0af0e34769483f69a80e266ae0a04d19b7d32609..680e510b74da1da23230bfa48f0385dc50262dc6 100644 (file)
@@ -2299,6 +2299,47 @@ produced by tree-sitter.  */)
 \f
 /*** Query functions */
 
+/* Convert a Lisp string to its printed representation in the tree-sitter
+   query syntax.  */
+static Lisp_Object
+treesit_query_string_string (Lisp_Object str)
+{
+  /* Strings in the treesit query syntax only have the escapes
+     \n \r \t \0 and any other escaped char stands for that character.
+     Literal LF, NUL and " are forbidden.  */
+  ptrdiff_t nbytes = SBYTES (str);
+  ptrdiff_t escapes = 0;
+  for (ptrdiff_t i = 0; i < nbytes; i++)
+    {
+      unsigned char c = SREF (str, i);
+      escapes += (c == '\0' || c == '\n' || c == '\r' || c == '\t' || c == '"');
+    }
+  ptrdiff_t nchars = SCHARS (str);
+  ptrdiff_t extra = escapes + 2;   /* backslashes + double quotes */
+  Lisp_Object dst = (STRING_MULTIBYTE (str)
+                    ? make_uninit_multibyte_string (nchars + extra,
+                                                    nbytes + extra)
+                    : make_uninit_string (nbytes + extra));
+  unsigned char *d = SDATA (dst);
+  *d++ = '"';
+  for (ptrdiff_t i = 0; i < nbytes; i++)
+    {
+      unsigned char c = SREF (str, i);
+      switch (c)
+       {
+       case '\0': *d++ = '\\'; *d++ = '0'; break;
+       case '\n': *d++ = '\\'; *d++ = 'n'; break;
+       case '\r': *d++ = '\\'; *d++ = 'r'; break;
+       case '\t': *d++ = '\\'; *d++ = 't'; break;
+       case '"':  *d++ = '\\'; *d++ = '"'; break;
+       default: *d++ = c; break;
+       }
+    }
+  *d++ = '"';
+  eassert (d == SDATA (dst) + SBYTES (dst));
+  return dst;
+}
+
 DEFUN ("treesit-pattern-expand",
        Ftreesit_pattern_expand,
        Streesit_pattern_expand, 1, 1, 0,
@@ -2349,6 +2390,9 @@ See Info node `(elisp)Pattern Matching' for detailed explanation.  */)
                                pattern,
                                Vtreesit_str_space),
                    closing_delimiter);
+  if (STRINGP (pattern))
+    return treesit_query_string_string (pattern);
+
   return Fprin1_to_string (pattern, Qnil, Qt);
 }
 
index 7a8e53924eb0538e95399237e5278bc66b1fb959..04aa91ddca6cbf26e3751e139e9045815d331e16 100644 (file)
@@ -461,7 +461,12 @@ BODY is the test body."
         "(type field: (_) @capture .) ? * + \"return\""
         (treesit-query-expand
          '((type field: (_) @capture :anchor)
-           :? :* :+ "return")))))))
+           :? :* :+ "return"))))
+
+      ;; Test string conversion in `treesit-pattern-expand'.
+      (should (equal
+               (treesit-pattern-expand "a\nb\rc\td\0e\"f\1g")
+               "\"a\\nb\\rc\\td\\0e\\\"f\1g\"")))))
 
 ;;; Narrow