Skip to content

Commit

Permalink
[Fix #377] Fix font locking for def forms (#630)
Browse files Browse the repository at this point in the history
  • Loading branch information
OknoLombarda authored Aug 30, 2022
1 parent 0af29f4 commit 68e0e13
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 33 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
Expand Down
42 changes: 34 additions & 8 deletions clojure-mode.el
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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/\\)?\\("
Expand All @@ -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)
Expand All @@ -861,21 +889,19 @@ 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]+"
;; Possibly type
"\\(?:#?^\\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)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down
56 changes: 33 additions & 23 deletions test/clojure-mode-font-lock-test.el
Original file line number Diff line number Diff line change
Expand Up @@ -776,24 +776,51 @@ 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)
(3 3 nil)
(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)
Expand Down Expand Up @@ -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)"
Expand Down

0 comments on commit 68e0e13

Please sign in to comment.