From f706d348014a75e662567d60ca894ded1caa3aa9 Mon Sep 17 00:00:00 2001 From: Vadim Rodionov Date: Tue, 30 Aug 2022 15:10:25 +0600 Subject: [PATCH 1/4] Fix all forms starting with 'def' being highlighted as def forms --- clojure-mode.el | 42 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 8 deletions(-) diff --git a/clojure-mode.el b/clojure-mode.el index 414d1ed9..30f6baca 100644 --- a/clojure-mode.el +++ b/clojure-mode.el @@ -825,7 +825,37 @@ any number of matches of `clojure--sym-forbidden-rest-chars'.")) (defconst clojure-font-lock-keywords (eval-when-compile - `( ;; Top-level variable definition + `(;; Any def form + (,(concat "(\\(?:" clojure--sym-regexp "/\\)?" + "\\(" + (regexp-opt '("def" + "defonce" + "defn" + "defn-" + "defmacro" + "definline" + "defmulti" + "defmethod" + "defprotocol" + "definterface" + "defrecord" + "deftype" + "defstruct" + ;; clojure.test + "deftest" + "deftest-" + ;; clojure.logic + "defne" + "defnm" + "defnu" + "defnc" + "defna" + ;; Third party + "deftask" + "defstate")) + "\\)\\>") + (1 font-lock-keyword-face)) + ;; Top-level variable definition (,(concat "(\\(?:clojure.core/\\)?\\(" (regexp-opt '("def" "defonce")) ;; variable declarations @@ -835,7 +865,6 @@ any number of matches of `clojure--sym-forbidden-rest-chars'.")) ;; Possibly type or metadata "\\(?:#?^\\(?:{[^}]*}\\|\\sw+\\)[ \r\n\t]*\\)*" "\\(\\sw+\\)?") - (1 font-lock-keyword-face) (2 font-lock-variable-name-face nil t)) ;; Type definition (,(concat "(\\(?:clojure.core/\\)?\\(" @@ -848,7 +877,6 @@ any number of matches of `clojure--sym-forbidden-rest-chars'.")) ;; Possibly type or metadata "\\(?:#?^\\(?:{[^}]*}\\|\\sw+\\)[ \r\n\t]*\\)*" "\\(\\sw+\\)?") - (1 font-lock-keyword-face) (2 font-lock-type-face nil t)) ;; Function definition (anything that starts with def and is not ;; listed above) @@ -861,7 +889,6 @@ any number of matches of `clojure--sym-forbidden-rest-chars'.")) ;; Possibly type or metadata "\\(?:#?^\\(?:{[^}]*}\\|\\sw+\\)[ \r\n\t]*\\)*" (concat "\\(" clojure--sym-regexp "\\)?")) - (1 font-lock-keyword-face) (2 font-lock-function-name-face nil t)) ;; (fn name? args ...) (,(concat "(\\(?:clojure.core/\\)?\\(fn\\)[ \t]+" @@ -869,13 +896,12 @@ any number of matches of `clojure--sym-forbidden-rest-chars'.")) "\\(?:#?^\\sw+[ \t]*\\)?" ;; Possibly name "\\(\\sw+\\)?" ) - (1 font-lock-keyword-face) (2 font-lock-function-name-face nil t)) ;; Special forms (,(concat "(" (regexp-opt - '("def" "do" "if" "let*" "var" "fn" "fn*" "loop*" + '("do" "if" "let*" "var" "fn" "fn*" "loop*" "recur" "throw" "try" "catch" "finally" "set!" "new" "." "monitor-enter" "monitor-exit" "quote") t) @@ -957,7 +983,7 @@ any number of matches of `clojure--sym-forbidden-rest-chars'.")) "with-redefs" "with-redefs-fn" ) - t) + t) "\\>") 1 font-lock-keyword-face) ;; Macros similar to let, when, and while @@ -1057,7 +1083,7 @@ any number of matches of `clojure--sym-forbidden-rest-chars'.")) (1 'font-lock-constant-face prepend)) ;; Highlight [[var]] comments (,(rx "[[" (group-n 1 (optional "#'") - (+ (or (syntax symbol) (syntax word)))) "]]") + (+ (or (syntax symbol) (syntax word)))) "]]") (1 'font-lock-constant-face prepend)) ;; Highlight escaped characters in strings. (clojure-font-lock-escaped-chars 0 'bold prepend) From 5b9531250e3905efb3e129df589cda26b4d342d4 Mon Sep 17 00:00:00 2001 From: Vadim Rodionov Date: Tue, 30 Aug 2022 15:10:43 +0600 Subject: [PATCH 2/4] Add more test cases for def forms font locking --- test/clojure-mode-font-lock-test.el | 56 +++++++++++++++++------------ 1 file changed, 33 insertions(+), 23 deletions(-) diff --git a/test/clojure-mode-font-lock-test.el b/test/clojure-mode-font-lock-test.el index c8304a05..cd4f21fc 100644 --- a/test/clojure-mode-font-lock-test.el +++ b/test/clojure-mode-font-lock-test.el @@ -776,17 +776,17 @@ DESCRIPTION is the description of the spec." (6 42 clojure-keyword-face))) (when-fontifying-it "should handle namespaced defs" - ("(_c4/defconstrainedfn bar [] nil)" + ("(_c4/defn bar [] nil)" (2 4 font-lock-type-face) (5 5 nil) - (6 18 font-lock-keyword-face) - (23 25 font-lock-function-name-face)) + (6 9 font-lock-keyword-face) + (11 13 font-lock-function-name-face)) - ("(clo/defbar foo nil)" + ("(clo/defrecord foo nil)" (2 4 font-lock-type-face) (5 5 nil) - (6 11 font-lock-keyword-face) - (13 15 font-lock-function-name-face)) + (6 14 font-lock-keyword-face) + (16 18 font-lock-function-name-face)) ("(s/def ::keyword)" (2 2 font-lock-type-face) @@ -794,6 +794,33 @@ DESCRIPTION is the description of the spec." (4 6 font-lock-keyword-face) (8 16 clojure-keyword-face))) + (when-fontifying-it "should handle any known def form" + ("(def a 1)" (2 4 font-lock-keyword-face)) + ("(defonce a 1)" (2 8 font-lock-keyword-face)) + ("(defn a [b])" (2 5 font-lock-keyword-face)) + ("(defmacro a [b])" (2 9 font-lock-keyword-face)) + ("(definline a [b])" (2 10 font-lock-keyword-face)) + ("(defmulti a identity)" (2 9 font-lock-keyword-face)) + ("(defmethod a :foo [b] (println \"bar\"))" (2 10 font-lock-keyword-face)) + ("(defprotocol a (b [this] \"that\"))" (2 12 font-lock-keyword-face)) + ("(definterface a (b [c]))" (2 13 font-lock-keyword-face)) + ("(defrecord a [b c])" (2 10 font-lock-keyword-face)) + ("(deftype a [b c])" (2 8 font-lock-keyword-face)) + ("(defstruct a :b :c)" (2 10 font-lock-keyword-face)) + ("(deftest a (is (= 1 1)))" (2 8 font-lock-keyword-face)) + ("(defne [x y])" (2 6 font-lock-keyword-face)) + ("(defnm a b)" (2 6 font-lock-keyword-face)) + ("(defnu)" (2 6 font-lock-keyword-face)) + ("(defnc [a])" (2 6 font-lock-keyword-face)) + ("(defna)" (2 6 font-lock-keyword-face)) + ("(deftask a)" (2 8 font-lock-keyword-face)) + ("(defstate a :start \"b\" :stop \"c\")" (2 9 font-lock-keyword-face))) + + (when-fontifying-it "should ignore unknown def forms" + ("(defbugproducer me)" (2 15 nil)) + ("(default-user-settings {:a 1})" (2 24 nil)) + ("(s/deftartar :foo)" (4 10 nil))) + (when-fontifying-it "should handle variables defined with def" ("(def foo 10)" (2 4 font-lock-keyword-face) @@ -845,23 +872,6 @@ DESCRIPTION is the description of the spec." (2 5 font-lock-keyword-face) (7 9 font-lock-function-name-face))) - (when-fontifying-it "should handle a custom def with special chars 1" - ("(defn* foo [x] x)" - (2 6 font-lock-keyword-face) - (8 10 font-lock-function-name-face))) - - (when-fontifying-it "should handle a custom def with special chars 2" - ("(defsomething! foo [x] x)" - (2 14 font-lock-keyword-face) - (16 18 font-lock-function-name-face))) - - (when-fontifying-it "should handle a custom def with special chars 3" - ("(def-something foo [x] x)" - (2 14 font-lock-keyword-face)) - - ("(def-something foo [x] x)" - (16 18 font-lock-function-name-face))) - (when-fontifying-it "should handle fn" ;; try to byte-recompile the clojure-mode.el when the face of 'fn' is 't' ("(fn foo [x] x)" From 3e1c4f68b149c2530fbb558173effebe9c825b58 Mon Sep 17 00:00:00 2001 From: Vadim Rodionov Date: Tue, 30 Aug 2022 15:16:42 +0600 Subject: [PATCH 3/4] Mention issue #377 in changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f10f099e..39c37b67 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ ### Bugs fixed * [#581](https://github.com/clojure-emacs/clojure-mode/issues/581): Fix font locking not working for keywords starting with a number. +* [#377](https://github.com/clojure-emacs/clojure-mode/issues/377): Fix everything starting with 'def' being highlighted as a def form. ## 5.15.1 (2022-07-30) From abec4857e5d03855df061da8d7a4514eb5618026 Mon Sep 17 00:00:00 2001 From: Vadim Rodionov Date: Tue, 30 Aug 2022 15:51:21 +0600 Subject: [PATCH 4/4] Describe in README how to add custom def forms --- README.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3997d63f..ec5ee369 100644 --- a/README.md +++ b/README.md @@ -305,11 +305,15 @@ conservative and minimalistic. Precise font-locking requires additional data that can obtained from a running REPL (that's how CIDER's [dynamic font-locking](https://docs.cider.mx/cider/config/syntax_highlighting.html) works) or from static code analysis. -When it comes to definitions, `clojure-mode` employs a simple heuristic and will treat every symbol named `def`something as a built-in keyword. Still, you'll need to -teach `clojure-mode` manually how to handle the docstrings of non built-in definition forms. Here's an example: +When it comes to non built-in definitions, `clojure-mode` needs to be manually instructed how to handle the docstrings and highlighting. Here's an example: ``` emacs-lisp (put '>defn 'clojure-doc-string-elt 2) + +(font-lock-add-keywords 'clojure-mode + `((,(concat "(\\(?:" clojure--sym-regexp "/\\)?" + "\\(>defn\\)\\>") + 1 font-lock-keyword-face))) ``` **Note:** The `clojure-doc-string-elt` attribute is processed by the function `clojure-font-lock-syntactic-face-function`.