diff --git a/CHANGELOG.md b/CHANGELOG.md index fff54a26..5c37033e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ * [#709](https://github.com/clojure-emacs/clojure-mode/pull/709): Fix `clojure-get-indent-spec` and `put-clojure-indent` converting legacy indent specs, like those of `letfn`, `reify` or `extend-protocol`, to the wrong modern rules. * [#709](https://github.com/clojure-emacs/clojure-mode/pull/709): Fix indenting with a modern indent spec that has an `:inner` rule with a position but no `:block` rule, like `((:inner 1 0))`, which signaled an error. * [#711](https://github.com/clojure-emacs/clojure-mode/pull/711): Fix the indentation of `letfn` functions, and of other forms an `:inner` rule with an index applies to, when a `#_` form comes before them, as the discarded form and its `#_` counted as two arguments. +* [#707](https://github.com/clojure-emacs/clojure-mode/issues/707): Highlight `[[var]]` and `` `code` `` only in strings and comments, so Datalog relation bindings like `[[?x]]` and destructuring like `(fn [[x]] x)` aren't colored as constants in code. ## 5.23.0 (2026-03-25) diff --git a/clojure-mode.el b/clojure-mode.el index 8b827353..73e49a5d 100644 --- a/clojure-mode.el +++ b/clojure-mode.el @@ -1226,12 +1226,18 @@ whether the paren is a direct child of a letfn binding vector." 'clojure-discard-face font-lock-comment-face) t) ;; Highlight `code` marks, just like `elisp'. - (,(rx "`" (group-n 1 (optional "#'") - (+ (or (syntax symbol) (syntax word)))) "`") + (,(lambda (bound) + (clojure--search-in-string-or-comment + (rx "`" (group-n 1 (optional "#'") + (+ (or (syntax symbol) (syntax word)))) "`") + bound)) (1 'font-lock-constant-face prepend)) ;; Highlight [[var]] comments - (,(rx "[[" (group-n 1 (optional "#'") - (+ (or (syntax symbol) (syntax word)))) "]]") + (,(lambda (bound) + (clojure--search-in-string-or-comment + (rx "[[" (group-n 1 (optional "#'") + (+ (or (syntax symbol) (syntax word)))) "]]") + bound)) (1 'font-lock-constant-face prepend)) ;; Highlight escaped characters in strings. (clojure-font-lock-escaped-chars 0 'bold prepend) @@ -1374,6 +1380,19 @@ preceded by a #." ;; When regexp is nil, match any string (both #"..." and "..."). string-beg)))))) +(defun clojure--search-in-string-or-comment (regexp bound) + "Search forward for REGEXP in a string or a comment, up to BOUND. +In code, the same text can be something else entirely, like the +[[?x]] relation binding of a Datalog query." + (let ((found nil)) + (while (and (not found) + (re-search-forward regexp bound t)) + (setq found (save-match-data + (save-excursion + (ppss-comment-or-string-start + (syntax-ppss (match-beginning 0))))))) + found)) + (defun clojure-font-lock-escaped-chars (bound) "Highlight \\escaped chars in strings. BOUND denotes a buffer position to limit the search." diff --git a/test/clojure-mode-font-lock-test.el b/test/clojure-mode-font-lock-test.el index e15ee277..6e8c0e2b 100644 --- a/test/clojure-mode-font-lock-test.el +++ b/test/clojure-mode-font-lock-test.el @@ -183,6 +183,16 @@ DESCRIPTION is the description of the spec." (4 11 (font-lock-constant-face font-lock-comment-face)) (12 13 font-lock-comment-face))) + (when-fontifying-it "should leave double brackets and backticks alone in code" + ("(fn [[x]] x)" + (7 7 nil)) + + ("[(q '[:find (min ?d)] $) [[?d]]]" + (28 29 nil)) + + ("(list `a`b)" + (8 8 nil))) + (when-fontifying-it "should fontify let, when, and while type forms" ("(when-alist [x 1]\n ())" ("when-alist" font-lock-keyword-face))