emacs-elpa-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[nongnu] elpa/clojure-ts-mode 569ed6c9a3: [#109] Fix performance issue b


From: ELPA Syncer
Subject: [nongnu] elpa/clojure-ts-mode 569ed6c9a3: [#109] Fix performance issue by pre-compiling Tree-sitter queries
Date: Thu, 5 Jun 2025 16:00:26 -0400 (EDT)

branch: elpa/clojure-ts-mode
commit 569ed6c9a3c6bb4f7dcc92090a6a8e582f63964e
Author: Roman Rudakov <rrudakov@fastmail.com>
Commit: Bozhidar Batsov <bozhidar@batsov.dev>

    [#109] Fix performance issue by pre-compiling Tree-sitter queries
    
    There is one trade-off: Markdown syntax won't be highlighted in docstrings 
of
    custom extra def forms.  I think it could be solved, but it would make the 
code
    more complicated.
---
 CHANGELOG.md                     |   2 +
 README.md                        |   4 +
 clojure-ts-mode.el               | 750 +++++++++++++++++++++------------------
 doc/design.md                    |   6 +
 test/samples/extra_def_forms.clj |   6 +
 5 files changed, 423 insertions(+), 345 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 09b696c3e5..a6fd4b3f62 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## main (unreleased)
 
+- [#109](https://github.com/clojure-emacs/clojure-ts-mode/issues/109): Improve 
performance by pre-compiling Tree-sitter queries.
+
 ## 0.5.0 (2025-06-04)
 
 - [#96](https://github.com/clojure-emacs/clojure-ts-mode/pull/96): Highlight 
function name properly in `extend-protocol` form.
diff --git a/README.md b/README.md
index 727abb25e6..b742c76e0e 100644
--- a/README.md
+++ b/README.md
@@ -591,6 +591,10 @@ and `clojure-mode` (this is very helpful when dealing with 
`derived-mode-p` chec
 - Navigation by sexp/lists might work differently on Emacs versions lower
   than 31. Starting with version 31, Emacs uses Tree-sitter 'things' settings, 
if
   available, to rebind some commands.
+- If you set `clojure-ts-extra-def-forms`, `clojure-ts-mode` will highlight the
+  specified forms, including their docstrings, in a manner similar to Clojure's
+  `defn`.  However, Markdown syntax will not be highlighted within these custom
+  docstrings.
 
 ## Frequently Asked Questions
 
diff --git a/clojure-ts-mode.el b/clojure-ts-mode.el
index 15dfef69c9..806723e652 100644
--- a/clojure-ts-mode.el
+++ b/clojure-ts-mode.el
@@ -260,12 +260,6 @@ values like this:
   :safe #'booleanp
   :type 'boolean)
 
-(defcustom clojure-ts-extra-def-forms nil
-  "List of forms that should be fontified the same way as defn."
-  :package-version '(clojure-ts-mode . "0.5")
-  :safe #'listp
-  :type '(repeat string))
-
 (defcustom clojure-ts-completion-enabled t
   "Enable built-in completion feature."
   :package-version '(clojure-ts-mode . "0.5")
@@ -438,67 +432,65 @@ if a third argument (the value) is provided.
   (rx line-start (or "defprotocol" "definterface") line-end)
   "A regular expression matching a symbol used to define an interface.")
 
-(defun clojure-ts--docstring-query (capture-symbol)
-  "Return a query that captures docstrings with CAPTURE-SYMBOL."
-  `(;; Captures docstrings in def
-    ((list_lit :anchor [(comment) (meta_lit) (old_meta_lit)] :*
-               :anchor (sym_lit) @_def_symbol
-               :anchor [(comment) (meta_lit) (old_meta_lit)] :*
-               ;; Variable name
-               :anchor (sym_lit)
-               :anchor [(comment) (meta_lit) (old_meta_lit)] :*
-               :anchor (str_lit (str_content) ,capture-symbol) 
@font-lock-doc-face
-               ;; The variable's value
-               :anchor (_))
-     (:match ,clojure-ts-definition-docstring-symbols
-             @_def_symbol))
-    ;; Captures docstrings in metadata of definitions
-    ((list_lit :anchor [(comment) (meta_lit) (old_meta_lit)] :*
-               :anchor (sym_lit) @_def_symbol
-               :anchor (comment) :*
-               :anchor (meta_lit
-                        value: (map_lit
-                                (kwd_lit) @_doc-keyword
-                                :anchor (str_lit (str_content) 
,capture-symbol) @font-lock-doc-face)))
-     ;; We're only supporting this on a fixed set of defining symbols
-     ;; Existing regexes don't encompass def and defn
-     ;; Naming another regex is very cumbersome.
-     (:match ,(clojure-ts-symbol-regexp
-               '("def" "defonce" "defn" "defn-" "defmacro" "ns"
-                 "defmulti" "definterface" "defprotocol"
-                 "deftest" "deftest-"
-                 "deftype" "defrecord" "defstruct"))
-             @_def_symbol)
-     (:equal @_doc-keyword ":doc"))
-    ;; Captures docstrings defn, defmacro, ns, and things like that
-    ((list_lit :anchor [(comment) (meta_lit) (old_meta_lit)] :*
-               :anchor (sym_lit) @_def_symbol
-               :anchor [(comment) (meta_lit) (old_meta_lit)] :*
-               ;; Function_name
-               :anchor (sym_lit)
-               :anchor [(comment) (meta_lit) (old_meta_lit)] :*
-               :anchor (str_lit (str_content) ,capture-symbol) 
@font-lock-doc-face)
-     (:match ,clojure-ts-function-docstring-symbols
-             @_def_symbol))
-    ((list_lit :anchor [(comment) (meta_lit) (old_meta_lit)] :*
-               :anchor (sym_lit) @_def_symbol
-               :anchor [(comment) (meta_lit) (old_meta_lit)] :*
-               ;; Function_name
-               :anchor (sym_lit)
-               :anchor [(comment) (meta_lit) (old_meta_lit)] :*
-               :anchor (str_lit (str_content) ,capture-symbol) 
@font-lock-doc-face)
-     (:match ,(clojure-ts-symbol-regexp clojure-ts-extra-def-forms)
-             @_def_symbol))
-    ;; Captures docstrings in defprotcol, definterface
-    ((list_lit :anchor [(comment) (meta_lit) (old_meta_lit)] :*
-               :anchor (sym_lit) @_def_symbol
-               (list_lit :anchor (sym_lit) (vec_lit) :*
-                         (str_lit (str_content) ,capture-symbol) 
@font-lock-doc-face)
-               :*)
-     (:match ,clojure-ts--interface-def-symbol-regexp @_def_symbol))))
+(defun clojure-ts--docstring-query (capture-symbol &optional capture-quotes)
+  "Return a query that captures docstrings with CAPTURE-SYMBOL.
+
+By default produced query captures only strings content, if optional
+CAPTURE-QUOTES argument is non-nil, then the entire string literals are
+captured including quotes."
+  (let ((quotes-symbol (if capture-quotes
+                           capture-symbol
+                         '@_ignore)))
+    `(;; Captures docstrings in def
+      ((list_lit :anchor [(comment) (meta_lit) (old_meta_lit)] :*
+                 :anchor (sym_lit) @_def_symbol
+                 :anchor [(comment) (meta_lit) (old_meta_lit)] :*
+                 ;; Variable name
+                 :anchor (sym_lit)
+                 :anchor [(comment) (meta_lit) (old_meta_lit)] :*
+                 :anchor (str_lit (str_content) ,capture-symbol) ,quotes-symbol
+                 ;; The variable's value
+                 :anchor (_))
+       (:match ,clojure-ts-definition-docstring-symbols
+               @_def_symbol))
+      ;; Captures docstrings in metadata of definitions
+      ((list_lit :anchor [(comment) (meta_lit) (old_meta_lit)] :*
+                 :anchor (sym_lit) @_def_symbol
+                 :anchor (comment) :*
+                 :anchor (meta_lit
+                          value: (map_lit
+                                  (kwd_lit) @_doc-keyword
+                                  :anchor (str_lit (str_content) 
,capture-symbol) ,quotes-symbol)))
+       ;; We're only supporting this on a fixed set of defining symbols
+       ;; Existing regexes don't encompass def and defn
+       ;; Naming another regex is very cumbersome.
+       (:match ,(clojure-ts-symbol-regexp
+                 '("def" "defonce" "defn" "defn-" "defmacro" "ns"
+                   "defmulti" "definterface" "defprotocol"
+                   "deftest" "deftest-"
+                   "deftype" "defrecord" "defstruct"))
+               @_def_symbol)
+       (:equal @_doc-keyword ":doc"))
+      ;; Captures docstrings defn, defmacro, ns, and things like that
+      ((list_lit :anchor [(comment) (meta_lit) (old_meta_lit)] :*
+                 :anchor (sym_lit) @_def_symbol
+                 :anchor [(comment) (meta_lit) (old_meta_lit)] :*
+                 ;; Function_name
+                 :anchor (sym_lit)
+                 :anchor [(comment) (meta_lit) (old_meta_lit)] :*
+                 :anchor (str_lit (str_content) ,capture-symbol) 
,quotes-symbol)
+       (:match ,clojure-ts-function-docstring-symbols
+               @_def_symbol))
+      ;; Captures docstrings in defprotcol, definterface
+      ((list_lit :anchor [(comment) (meta_lit) (old_meta_lit)] :*
+                 :anchor (sym_lit) @_def_symbol
+                 (list_lit :anchor (sym_lit) (vec_lit) :*
+                           (str_lit (str_content) ,capture-symbol) 
,quotes-symbol)
+                 :*)
+       (:match ,clojure-ts--interface-def-symbol-regexp @_def_symbol)))))
 
 (defconst clojure-ts--match-docstring-query
-  (treesit-query-compile 'clojure (clojure-ts--docstring-query 
'@font-lock-doc-face))
+  (treesit-query-compile 'clojure (clojure-ts--docstring-query 
'@font-lock-doc-face t))
   "Precompiled query that matches a Clojure docstring.")
 
 (defun clojure-ts--treesit-range-settings (use-markdown-inline use-regex)
@@ -553,6 +545,341 @@ and end of the NODE, so we ignore them."
                                      'font-lock-string-face
                                      override))))
 
+(defconst clojure-ts--clojure-font-lock-queries
+  (treesit-font-lock-rules
+   :feature 'string
+   :language 'clojure
+   '((str_lit open: _ @font-lock-string-face
+              (str_content) @clojure-ts--fontify-string
+              close: _ @font-lock-string-face)
+     (regex_lit) @font-lock-regexp-face)
+
+   :feature 'regex
+   :language 'clojure
+   :override t
+   '((regex_lit marker: _ @font-lock-property-face))
+
+   :feature 'number
+   :language 'clojure
+   '((num_lit) @font-lock-number-face)
+
+   :feature 'constant
+   :language 'clojure
+   '([(bool_lit) (nil_lit)] @font-lock-constant-face)
+
+   :feature 'char
+   :language 'clojure
+   '((char_lit) @clojure-ts-character-face)
+
+   :feature 'keyword
+   :language 'clojure
+   '((kwd_ns) @font-lock-type-face
+     (kwd_name) @clojure-ts-keyword-face
+     (kwd_lit
+      marker: _ @clojure-ts-keyword-face
+      delimiter: _ :? @default))
+
+   ;; Highlight as built-in only if there is no namespace or namespace is
+   ;; `clojure.core'.
+   :feature 'builtin
+   :language 'clojure
+   `(((list_lit :anchor [(comment) (meta_lit) (old_meta_lit)] :*
+                :anchor (sym_lit !namespace name: (sym_name) 
@font-lock-keyword-face))
+      (:match ,clojure-ts--builtin-symbol-regexp @font-lock-keyword-face))
+     ((list_lit :anchor [(comment) (meta_lit) (old_meta_lit)] :*
+                :anchor (sym_lit namespace: ((sym_ns) @ns
+                                             (:equal "clojure.core" @ns))
+                                 name: (sym_name) @font-lock-keyword-face))
+      (:match ,clojure-ts--builtin-symbol-regexp @font-lock-keyword-face))
+     ((anon_fn_lit :anchor [(comment) (meta_lit) (old_meta_lit)] :*
+                   :anchor (sym_lit !namespace name: (sym_name) 
@font-lock-keyword-face))
+      (:match ,clojure-ts--builtin-symbol-regexp @font-lock-keyword-face))
+     ((anon_fn_lit :anchor [(comment) (meta_lit) (old_meta_lit)] :*
+                   :anchor (sym_lit namespace: ((sym_ns) @ns
+                                                (:equal "clojure.core" @ns))
+                                    name: (sym_name) @font-lock-keyword-face))
+      (:match ,clojure-ts--builtin-symbol-regexp @font-lock-keyword-face))
+     ((sym_name) @font-lock-builtin-face
+      (:match ,clojure-ts--builtin-dynamic-var-regexp 
@font-lock-builtin-face)))
+
+   ;; Any function calls, not built-ins.
+   ;; This can give false positives (macros, quoted lists, namespace imports)
+   ;; but is a level 4 feature and never enabled by default.
+   :feature 'function
+   :language 'clojure
+   '((list_lit :anchor (sym_lit (sym_name) @font-lock-function-call-face)))
+
+   :feature 'symbol
+   :language 'clojure
+   '((sym_ns) @font-lock-type-face)
+
+   ;; How does this work for defns nested in other forms, not at the top level?
+   ;; Should I match against the source node to only hit the top level? Can 
that be expressed?
+   ;; What about valid usages like `(let [closed 1] (defn +closed [n] (+ n 
closed)))'??
+   ;; No wonder the tree-sitter-clojure grammar only touches syntax, and not 
semantics
+   :feature 'definition ;; defn and defn like macros
+   :language 'clojure
+   `(((list_lit :anchor [(comment) (meta_lit) (old_meta_lit)] :*
+                :anchor (sym_lit (sym_name) @font-lock-keyword-face)
+                :anchor [(comment) (meta_lit) (old_meta_lit)] :*
+                :anchor (sym_lit (sym_name) @font-lock-function-name-face))
+      (:match ,(rx-to-string
+                `(seq bol
+                      (or
+                       "fn"
+                       "defn"
+                       "defn-"
+                       "defmulti"
+                       "defmethod"
+                       "deftest"
+                       "deftest-"
+                       "defmacro"
+                       "definline"
+                       "defonce")
+                      eol))
+              @font-lock-keyword-face))
+     ((anon_fn_lit
+       marker: "#" @font-lock-property-face))
+     ;; Methods implementation
+     ((list_lit
+       :anchor [(comment) (meta_lit) (old_meta_lit)] :*
+       :anchor ((sym_lit name: (sym_name) @def)
+                ((:match ,(rx-to-string
+                           `(seq bol
+                                 (or
+                                  "defrecord"
+                                  "definterface"
+                                  "deftype"
+                                  "defprotocol")
+                                 eol))
+                         @def)))
+       :anchor [(comment) (meta_lit) (old_meta_lit)] :*
+       :anchor (sym_lit (sym_name) @font-lock-type-face)
+       (list_lit
+        (sym_lit name: (sym_name) @font-lock-function-name-face))))
+     ((list_lit
+       ((sym_lit name: (sym_name) @def)
+        ((:match ,(rx-to-string
+                   `(seq bol
+                         (or "reify"
+                             "extend-protocol"
+                             "extend-type")
+                         eol))
+                 @def)))
+       (list_lit
+        (sym_lit name: (sym_name) @font-lock-function-name-face))))
+     ;; letfn
+     ((list_lit
+       ((sym_lit name: (sym_name) @symbol)
+        ((:equal "letfn" @symbol)))
+       (vec_lit
+        (list_lit
+         (sym_lit name: (sym_name) @font-lock-function-name-face))))))
+
+   :feature 'variable ;; def, defonce
+   :language 'clojure
+   `(((list_lit :anchor [(comment) (meta_lit) (old_meta_lit)] :*
+                :anchor (sym_lit (sym_name) @font-lock-keyword-face)
+                :anchor [(comment) (meta_lit) (old_meta_lit)] :*
+                :anchor (sym_lit (sym_name) @font-lock-variable-name-face))
+      (:match ,clojure-ts--variable-definition-symbol-regexp 
@font-lock-keyword-face)))
+
+   ;; Can we support declarations in the namespace form?
+   :feature 'type
+   :language 'clojure
+   `(;; Type Declarations
+     ((list_lit :anchor (sym_lit (sym_name) @def)
+                :anchor (sym_lit (sym_name) @font-lock-type-face))
+      (:match ,clojure-ts--typedef-symbol-regexp @def))
+     ;; Type Hints
+     (meta_lit
+      marker: "^" @font-lock-operator-face
+      value: (sym_lit (sym_name) @font-lock-type-face))
+     (old_meta_lit
+      marker: "#^" @font-lock-operator-face
+      value: (sym_lit (sym_name) @font-lock-type-face))
+     ;; Highlight namespace
+     ((list_lit :anchor (sym_lit (sym_name) @def)
+                :anchor (sym_lit (sym_name) @font-lock-type-face))
+      (:equal "ns" @def)))
+
+   :feature 'metadata
+   :language 'clojure
+   :override t
+   `((meta_lit
+      marker: "^" @font-lock-operator-face
+      value: (kwd_lit (kwd_name) @clojure-ts-keyword-face))
+     (old_meta_lit
+      marker: "#^" @font-lock-operator-face
+      value: (kwd_lit (kwd_name) @clojure-ts-keyword-face)))
+
+   :feature 'tagged-literals
+   :language 'clojure
+   :override t
+   '((tagged_or_ctor_lit marker: "#" @font-lock-preprocessor-face
+                         tag: (sym_lit) @font-lock-preprocessor-face))
+
+   :feature 'doc
+   :language 'clojure
+   :override t
+   (clojure-ts--docstring-query '@font-lock-doc-face t)
+
+   :feature 'quote
+   :language 'clojure
+   '((quoting_lit
+      marker: _ @font-lock-delimiter-face)
+     (var_quoting_lit
+      marker: _ @font-lock-delimiter-face)
+     (syn_quoting_lit
+      marker: _ @font-lock-delimiter-face)
+     (unquoting_lit
+      marker: _ @font-lock-delimiter-face)
+     (unquote_splicing_lit
+      marker: _ @font-lock-delimiter-face)
+     (var_quoting_lit
+      marker: _ @font-lock-delimiter-face))
+
+   :feature 'bracket
+   :language 'clojure
+   '((["(" ")" "[" "]" "{" "}"]) @font-lock-bracket-face
+     (set_lit :anchor "#" @font-lock-bracket-face))
+
+   :feature 'comment
+   :language 'clojure
+   :override t
+   `((comment) @font-lock-comment-face
+     (dis_expr
+      marker: "#_" @font-lock-comment-delimiter-face
+      meta: (meta_lit) :* @font-lock-comment-face
+      value: _ @font-lock-comment-face)
+     (,(append
+        '(list_lit :anchor (sym_lit) @font-lock-comment-delimiter-face)
+        (when clojure-ts-comment-macro-font-lock-body
+          '(_ :* @font-lock-comment-face)))
+      (:match "^\\(\\(clojure.core/\\)?comment\\)$" 
@font-lock-comment-delimiter-face)))
+
+   :feature 'deref ;; not part of clojure-mode, but a cool idea?
+   :language 'clojure
+   '((derefing_lit
+      marker: "@" @font-lock-warning-face))))
+
+(defvar clojure-ts--clojure-extra-queries nil
+  "Pre-compiled Tree-sitter queries produced from 
`clojure-ts-extra-def-forms'.")
+
+(defun clojure-ts--compute-extra-def-queries (syms)
+  "Comute font lock rules for extra def forms.
+
+If SYMS are not provided, return nil.  If SYMS are provided, this
+function returns compiled font lock rules that should be assigned to
+`clojure-ts--clojure-extra-queries' variable.
+
+This function is called when the `clojure-ts-extra-def-forms' variable
+is customized using setopt or the Emacs customization interface.  It is
+also called when file-local variables are updated.  This ensures that
+updated indentation rules are always precalculated."
+  (when syms
+    (treesit-font-lock-rules
+     :feature 'definition
+     :language 'clojure
+     `(((list_lit :anchor [(comment) (meta_lit) (old_meta_lit)] :*
+                  :anchor (sym_lit (sym_name) @font-lock-keyword-face)
+                  :anchor [(comment) (meta_lit) (old_meta_lit)] :*
+                  :anchor (sym_lit (sym_name) @font-lock-function-name-face))
+        (:match ,(clojure-ts-symbol-regexp syms)
+                @font-lock-keyword-face)))
+
+     ;; NOTE: Here we also define queries to fontify docstrings in custom extra
+     ;; defn forms, but Markdown syntax won't work here, because it's not a 
part
+     ;; of range settings.
+     :feature 'doc
+     :language 'clojure
+     :override t
+     `(((list_lit :anchor [(comment) (meta_lit) (old_meta_lit)] :*
+                  :anchor (sym_lit) @_def_symbol
+                  :anchor [(comment) (meta_lit) (old_meta_lit)] :*
+                  ;; Function_name
+                  :anchor (sym_lit)
+                  :anchor [(comment) (meta_lit) (old_meta_lit)] :*
+                  :anchor (str_lit) @font-lock-doc-face)
+        (:match ,(clojure-ts-symbol-regexp syms)
+                @_def_symbol))))))
+
+(defun clojure-ts--set-extra-def-queries (symbol value)
+  "Setter function for `clojure-ts-extra-def-forms' variable.
+
+Sets SYMBOL's top-level default value to VALUE and updates the
+`clojure-ts--clojure-extra-queries' in all `clojure-ts-mode'
+buffers, if any exist.
+
+NOTE: This function is not meant to be called directly."
+  (set-default-toplevel-value symbol value)
+  ;; Update value in every `clojure-ts-mode' buffer.
+  (let ((new-value (clojure-ts--compute-extra-def-queries value)))
+    (dolist (buf (buffer-list))
+      (when (buffer-local-boundp 'clojure-ts--clojure-extra-queries buf)
+        (setq clojure-ts--clojure-extra-queries new-value)))))
+
+(defcustom clojure-ts-extra-def-forms nil
+  "List of forms that should be fontified the same way as defn."
+  :package-version '(clojure-ts-mode . "0.5")
+  :safe #'listp
+  :type '(repeat string)
+  :set #'clojure-ts--set-extra-def-queries)
+
+(defconst clojure-ts--markdown-font-lock-queries
+  (treesit-font-lock-rules
+   :feature 'doc
+   :language 'markdown-inline
+   :override 'prepend
+   `([((image_description) @link)
+      ((link_destination) @font-lock-constant-face)
+      ((code_span) @font-lock-constant-face)
+      ((emphasis) @underline)
+      ((strong_emphasis) @bold)
+      (inline_link (link_text) @link)
+      (inline_link (link_destination) @font-lock-constant-face)
+      (shortcut_link (link_text) @link)])))
+
+(defconst clojure-ts--regex-font-lock-queries
+  ;; Queries are adapted from
+  ;; 
https://github.com/tree-sitter/tree-sitter-regex/blob/v0.24.3/queries/highlights.scm.
+  (treesit-font-lock-rules
+   :feature 'regex
+   :language 'regex
+   :override t
+   '((["("
+       ")"
+       "(?"
+       "(?:"
+       "(?<"
+       "(?P<"
+       "(?P="
+       ">"
+       "["
+       "]"
+       "{"
+       "}"
+       "[:"
+       ":]"]
+      @font-lock-regexp-grouping-construct)
+     (["*"
+       "+"
+       "?"
+       "|"
+       "="
+       "!"]
+      @font-lock-property-name-face)
+     ((group_name) @font-lock-variable-name-face)
+     ((count_quantifier
+       [(decimal_digits) @font-lock-number-face
+        "," @font-lock-delimiter-face]))
+     ((flags) @font-lock-constant-face)
+     ((character_class
+       ["^" @font-lock-escape-face
+        (class_range "-" @font-lock-escape-face)]))
+     ((identity_escape) @font-lock-builtin-face)
+     ([(start_assertion) (end_assertion)] @font-lock-constant-face))))
+
 (defun clojure-ts--font-lock-settings (markdown-available regex-available)
   "Return font lock settings suitable for use in `treesit-font-lock-settings'.
 
@@ -561,282 +888,12 @@ with the markdown-inline grammar.
 
 When REGEX-AVAILABLE is non-nil, includes rules for highlighting regex
 literals with regex grammar."
-  (append
-   (treesit-font-lock-rules
-    :feature 'string
-    :language 'clojure
-    '((str_lit open: _ @font-lock-string-face
-               (str_content) @clojure-ts--fontify-string
-               close: _ @font-lock-string-face)
-      (regex_lit) @font-lock-regexp-face)
-
-    :feature 'regex
-    :language 'clojure
-    :override t
-    '((regex_lit marker: _ @font-lock-property-face))
-
-    :feature 'number
-    :language 'clojure
-    '((num_lit) @font-lock-number-face)
-
-    :feature 'constant
-    :language 'clojure
-    '([(bool_lit) (nil_lit)] @font-lock-constant-face)
-
-    :feature 'char
-    :language 'clojure
-    '((char_lit) @clojure-ts-character-face)
-
-    :feature 'keyword
-    :language 'clojure
-    '((kwd_ns) @font-lock-type-face
-      (kwd_name) @clojure-ts-keyword-face
-      (kwd_lit
-       marker: _ @clojure-ts-keyword-face
-       delimiter: _ :? @default))
-
-    ;; Highlight as built-in only if there is no namespace or namespace is
-    ;; `clojure.core'.
-    :feature 'builtin
-    :language 'clojure
-    `(((list_lit :anchor [(comment) (meta_lit) (old_meta_lit)] :*
-                 :anchor (sym_lit !namespace name: (sym_name) 
@font-lock-keyword-face))
-       (:match ,clojure-ts--builtin-symbol-regexp @font-lock-keyword-face))
-      ((list_lit :anchor [(comment) (meta_lit) (old_meta_lit)] :*
-                 :anchor (sym_lit namespace: ((sym_ns) @ns
-                                              (:equal "clojure.core" @ns))
-                                  name: (sym_name) @font-lock-keyword-face))
-       (:match ,clojure-ts--builtin-symbol-regexp @font-lock-keyword-face))
-      ((anon_fn_lit :anchor [(comment) (meta_lit) (old_meta_lit)] :*
-                    :anchor (sym_lit !namespace name: (sym_name) 
@font-lock-keyword-face))
-       (:match ,clojure-ts--builtin-symbol-regexp @font-lock-keyword-face))
-      ((anon_fn_lit :anchor [(comment) (meta_lit) (old_meta_lit)] :*
-                    :anchor (sym_lit namespace: ((sym_ns) @ns
-                                                 (:equal "clojure.core" @ns))
-                                     name: (sym_name) @font-lock-keyword-face))
-       (:match ,clojure-ts--builtin-symbol-regexp @font-lock-keyword-face))
-      ((sym_name) @font-lock-builtin-face
-       (:match ,clojure-ts--builtin-dynamic-var-regexp 
@font-lock-builtin-face)))
-
-    ;; Any function calls, not built-ins.
-    ;; This can give false positives (macros, quoted lists, namespace imports)
-    ;; but is a level 4 feature and never enabled by default.
-    :feature 'function
-    :language 'clojure
-    '((list_lit :anchor (sym_lit (sym_name) @font-lock-function-call-face)))
-
-    :feature 'symbol
-    :language 'clojure
-    '((sym_ns) @font-lock-type-face)
-
-    ;; How does this work for defns nested in other forms, not at the top 
level?
-    ;; Should I match against the source node to only hit the top level? Can 
that be expressed?
-    ;; What about valid usages like `(let [closed 1] (defn +closed [n] (+ n 
closed)))'??
-    ;; No wonder the tree-sitter-clojure grammar only touches syntax, and not 
semantics
-    :feature 'definition ;; defn and defn like macros
-    :language 'clojure
-    `(((list_lit :anchor [(comment) (meta_lit) (old_meta_lit)] :*
-                 :anchor (sym_lit (sym_name) @font-lock-keyword-face)
-                 :anchor [(comment) (meta_lit) (old_meta_lit)] :*
-                 :anchor (sym_lit (sym_name) @font-lock-function-name-face))
-       (:match ,(rx-to-string
-                 `(seq bol
-                       (or
-                        "fn"
-                        "defn"
-                        "defn-"
-                        "defmulti"
-                        "defmethod"
-                        "deftest"
-                        "deftest-"
-                        "defmacro"
-                        "definline"
-                        "defonce")
-                       eol))
-               @font-lock-keyword-face))
-      ((list_lit :anchor [(comment) (meta_lit) (old_meta_lit)] :*
-                 :anchor (sym_lit (sym_name) @font-lock-keyword-face)
-                 :anchor [(comment) (meta_lit) (old_meta_lit)] :*
-                 :anchor (sym_lit (sym_name) @font-lock-function-name-face))
-       (:match ,(clojure-ts-symbol-regexp clojure-ts-extra-def-forms)
-               @font-lock-keyword-face))
-      ((anon_fn_lit
-        marker: "#" @font-lock-property-face))
-      ;; Methods implementation
-      ((list_lit
-        :anchor [(comment) (meta_lit) (old_meta_lit)] :*
-        :anchor ((sym_lit name: (sym_name) @def)
-                 ((:match ,(rx-to-string
-                            `(seq bol
-                                  (or
-                                   "defrecord"
-                                   "definterface"
-                                   "deftype"
-                                   "defprotocol")
-                                  eol))
-                          @def)))
-        :anchor [(comment) (meta_lit) (old_meta_lit)] :*
-        :anchor (sym_lit (sym_name) @font-lock-type-face)
-        (list_lit
-         (sym_lit name: (sym_name) @font-lock-function-name-face))))
-      ((list_lit
-        ((sym_lit name: (sym_name) @def)
-         ((:match ,(rx-to-string
-                    `(seq bol
-                          (or "reify"
-                              "extend-protocol"
-                              "extend-type")
-                          eol))
-                  @def)))
-        (list_lit
-         (sym_lit name: (sym_name) @font-lock-function-name-face))))
-      ;; letfn
-      ((list_lit
-        ((sym_lit name: (sym_name) @symbol)
-         ((:equal "letfn" @symbol)))
-        (vec_lit
-         (list_lit
-          (sym_lit name: (sym_name) @font-lock-function-name-face))))))
-
-    :feature 'variable ;; def, defonce
-    :language 'clojure
-    `(((list_lit :anchor [(comment) (meta_lit) (old_meta_lit)] :*
-                 :anchor (sym_lit (sym_name) @font-lock-keyword-face)
-                 :anchor [(comment) (meta_lit) (old_meta_lit)] :*
-                 :anchor (sym_lit (sym_name) @font-lock-variable-name-face))
-       (:match ,clojure-ts--variable-definition-symbol-regexp 
@font-lock-keyword-face)))
-
-    ;; Can we support declarations in the namespace form?
-    :feature 'type
-    :language 'clojure
-    `(;; Type Declarations
-      ((list_lit :anchor (sym_lit (sym_name) @def)
-                 :anchor (sym_lit (sym_name) @font-lock-type-face))
-       (:match ,clojure-ts--typedef-symbol-regexp @def))
-      ;; Type Hints
-      (meta_lit
-       marker: "^" @font-lock-operator-face
-       value: (sym_lit (sym_name) @font-lock-type-face))
-      (old_meta_lit
-       marker: "#^" @font-lock-operator-face
-       value: (sym_lit (sym_name) @font-lock-type-face))
-      ;; Highlight namespace
-      ((list_lit :anchor (sym_lit (sym_name) @def)
-                 :anchor (sym_lit (sym_name) @font-lock-type-face))
-       (:equal "ns" @def)))
-
-    :feature 'metadata
-    :language 'clojure
-    :override t
-    `((meta_lit
-       marker: "^" @font-lock-operator-face
-       value: (kwd_lit (kwd_name) @clojure-ts-keyword-face))
-      (old_meta_lit
-       marker: "#^" @font-lock-operator-face
-       value: (kwd_lit (kwd_name) @clojure-ts-keyword-face)))
-
-    :feature 'tagged-literals
-    :language 'clojure
-    :override t
-    '((tagged_or_ctor_lit marker: "#" @font-lock-preprocessor-face
-                          tag: (sym_lit) @font-lock-preprocessor-face))
-
-    :feature 'doc
-    :language 'clojure
-    :override t
-    (clojure-ts--docstring-query '@font-lock-doc-face))
-
-   (when markdown-available
-     (treesit-font-lock-rules
-      :feature 'doc
-      :language 'markdown-inline
-      :override 'prepend
-      `([((image_description) @link)
-         ((link_destination) @font-lock-constant-face)
-         ((code_span) @font-lock-constant-face)
-         ((emphasis) @underline)
-         ((strong_emphasis) @bold)
-         (inline_link (link_text) @link)
-         (inline_link (link_destination) @font-lock-constant-face)
-         (shortcut_link (link_text) @link)])))
-
-   (when regex-available
-     ;; Queries are adapted from
-     ;; 
https://github.com/tree-sitter/tree-sitter-regex/blob/v0.24.3/queries/highlights.scm.
-     (treesit-font-lock-rules
-      :feature 'regex
-      :language 'regex
-      :override t
-      '((["("
-           ")"
-           "(?"
-           "(?:"
-           "(?<"
-           "(?P<"
-           "(?P="
-           ">"
-           "["
-           "]"
-           "{"
-           "}"
-           "[:"
-           ":]"] @font-lock-regexp-grouping-construct)
-         (["*"
-           "+"
-           "?"
-           "|"
-           "="
-           "!"] @font-lock-property-name-face)
-         ((group_name) @font-lock-variable-name-face)
-         ((count_quantifier
-           [(decimal_digits) @font-lock-number-face
-            "," @font-lock-delimiter-face]))
-         ((flags) @font-lock-constant-face)
-         ((character_class
-           ["^" @font-lock-escape-face
-            (class_range "-" @font-lock-escape-face)]))
-         ((identity_escape) @font-lock-builtin-face)
-         ([(start_assertion) (end_assertion)] @font-lock-constant-face))))
-
-   (treesit-font-lock-rules
-    :feature 'quote
-    :language 'clojure
-    '((quoting_lit
-       marker: _ @font-lock-delimiter-face)
-      (var_quoting_lit
-       marker: _ @font-lock-delimiter-face)
-      (syn_quoting_lit
-       marker: _ @font-lock-delimiter-face)
-      (unquoting_lit
-       marker: _ @font-lock-delimiter-face)
-      (unquote_splicing_lit
-       marker: _ @font-lock-delimiter-face)
-      (var_quoting_lit
-       marker: _ @font-lock-delimiter-face))
-
-    :feature 'bracket
-    :language 'clojure
-    '((["(" ")" "[" "]" "{" "}"]) @font-lock-bracket-face
-      (set_lit :anchor "#" @font-lock-bracket-face))
-
-    :feature 'comment
-    :language 'clojure
-    :override t
-    `((comment) @font-lock-comment-face
-      (dis_expr
-       marker: "#_" @font-lock-comment-delimiter-face
-       meta: (meta_lit) :* @font-lock-comment-face
-       value: _ @font-lock-comment-face)
-      (,(append
-         '(list_lit :anchor (sym_lit) @font-lock-comment-delimiter-face)
-         (when clojure-ts-comment-macro-font-lock-body
-           '(_ :* @font-lock-comment-face)))
-       (:match "^\\(\\(clojure.core/\\)?comment\\)$" 
@font-lock-comment-delimiter-face)))
-
-    :feature 'deref ;; not part of clojure-mode, but a cool idea?
-    :language 'clojure
-    '((derefing_lit
-       marker: "@" @font-lock-warning-face)))))
+  (append clojure-ts--clojure-font-lock-queries
+          clojure-ts--clojure-extra-queries
+          (when markdown-available
+            clojure-ts--markdown-font-lock-queries)
+          (when regex-available
+            clojure-ts--regex-font-lock-queries)))
 
 ;; Node predicates
 
@@ -1515,7 +1572,7 @@ It is simply `clojure-ts-docstring-fill-prefix-width' 
number of spaces."
 (defun clojure-ts--fill-paragraph (&optional justify)
   "Like `fill-paragraph', but can handler Clojure docstrings.
 If JUSTIFY is non-nil, justify as well as fill the paragraph."
-  (let ((current-node (treesit-node-at (point) 'clojure)))
+  (let ((current-node (treesit-node-at (point) 'clojure t)))
     (if (clojure-ts--match-docstring nil current-node nil)
         (let ((fill-column (or clojure-ts-docstring-fill-column fill-column))
               (fill-prefix (clojure-ts--docstring-fill-prefix))
@@ -2839,12 +2896,15 @@ REGEX-AVAILABLE."
       (setq clojure-ts--semantic-indent-rules-cache
             (clojure-ts--compute-semantic-indentation-rules-cache 
clojure-ts-semantic-indent-rules))
 
-      ;; If indentation rules are set in `.dir-locals.el', it is advisable to
-      ;; recalculate the buffer-local value whenever the value changes.
+      ;; If indentation rules or extra def forms are set in `.dir-locals.el', 
it
+      ;; is advisable to recalculate the buffer-local value whenever the value
+      ;; changes.
       (add-hook 'hack-local-variables-hook
                 (lambda ()
                   (setq clojure-ts--semantic-indent-rules-cache
-                        (clojure-ts--compute-semantic-indentation-rules-cache 
clojure-ts-semantic-indent-rules)))
+                        (clojure-ts--compute-semantic-indentation-rules-cache 
clojure-ts-semantic-indent-rules)
+                        clojure-ts--clojure-extra-queries
+                        (clojure-ts--compute-extra-def-queries 
clojure-ts-extra-def-forms)))
                 0
                 t)
 
diff --git a/doc/design.md b/doc/design.md
index e1d6b05544..34256191ad 100644
--- a/doc/design.md
+++ b/doc/design.md
@@ -207,6 +207,12 @@ metadata nodes) does not have a namespace and matches a 
regex stored in the
 `clojure-ts--builtin-symbol-regexp` variable.  The matched symbol is fontified
 using `font-lock-keyword-face`.
 
+> [!IMPORTANT]
+>
+> Compiling queries at runtime is very expensive; therefore, it should be
+> avoided as much as possible.  Ideally, all queries should be pre-compiled and
+> stored as `defconst` constants.
+
 ### Embedded parsers
 
 The Clojure grammar in `clojure-ts-mode` is a main or "host" grammar.  Emacs
diff --git a/test/samples/extra_def_forms.clj b/test/samples/extra_def_forms.clj
new file mode 100644
index 0000000000..6ecb3a3d7c
--- /dev/null
+++ b/test/samples/extra_def_forms.clj
@@ -0,0 +1,6 @@
+(ns extra-def-forms)
+
+(defelem file-upload
+  "Creates a file upload input."
+  [name]
+  (input-field "file" name nil))



reply via email to

[Prev in Thread] Current Thread [Next in Thread]