Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Fix #377] Fix font locking for def forms #630

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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