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 font locking of defintions with metadata #55

Merged
merged 5 commits into from
Oct 27, 2024
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 @@ -7,6 +7,7 @@
- Add imenu support for `deftest` definitions.
- [#53]: Let `clojure-ts-mode` derive from `clojure-mode` for Emacs 30+.
- [#42]: Fix imenu support for definitions with metadata.
- [#42]: Fix font locking of definitions with metadata

## 0.2.2 (2024-02-16)

Expand Down
14 changes: 9 additions & 5 deletions clojure-ts-mode.el
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,8 @@ if a third argument (the value) is provided.
(defun clojure-ts--docstring-query (capture-symbol)
"Return a query that captures docstrings with CAPTURE-SYMBOL."
`(;; Captures docstrings in def
((list_lit :anchor (sym_lit) @_def_symbol
((list_lit :anchor (meta_lit) :?
:anchor (sym_lit) @_def_symbol
:anchor (comment) :?
:anchor (sym_lit) ; variable name
:anchor (comment) :?
Expand Down Expand Up @@ -288,7 +289,8 @@ if a third argument (the value) is provided.
@_def_symbol)
(:equal @_doc-keyword ":doc"))
;; Captures docstrings defn, defmacro, ns, and things like that
((list_lit :anchor (sym_lit) @_def_symbol
((list_lit :anchor (meta_lit) :?
:anchor (sym_lit) @_def_symbol
:anchor (comment) :?
:anchor (sym_lit) ; function_name
:anchor (comment) :?
Expand Down Expand Up @@ -347,7 +349,7 @@ with the markdown_inline grammar."

:feature 'builtin
:language 'clojure
`(((list_lit :anchor (sym_lit (sym_name) @font-lock-keyword-face))
`(((list_lit meta: _ :? :anchor (sym_lit (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)))
Expand All @@ -369,7 +371,8 @@ with the markdown_inline grammar."
;; 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 (sym_lit (sym_name) @def)
`(((list_lit :anchor meta: _ :?
:anchor (sym_lit (sym_name) @def)
:anchor (sym_lit (sym_name) @font-lock-function-name-face))
(:match ,(rx-to-string
`(seq bol
Expand Down Expand Up @@ -410,7 +413,8 @@ with the markdown_inline grammar."

:feature 'variable ;; def, defonce
:language 'clojure
`(((list_lit :anchor (sym_lit (sym_name) @def)
`(((list_lit :anchor meta: _ :?
:anchor (sym_lit (sym_name) @def)
:anchor (sym_lit (sym_name) @font-lock-variable-name-face))
(:match ,clojure-ts--variable-definition-symbol-regexp @def)))

Expand Down
139 changes: 139 additions & 0 deletions test/clojure-ts-mode-font-lock-test.el
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
;;; clojure-ts-mode-font-lock-test.el --- Clojure TS Mode: font lock test suite -*- lexical-binding: t; -*-

;; Copyright © 2022-2024 Danny Freeman

;; This file is not part of GNU Emacs.

;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.

;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <https://www.gnu.org/licenses/>.

;;; Commentary:

;; The unit test suite of Clojure TS Mode

(require 'clojure-ts-mode)
(require 'cl-lib)
(require 'buttercup)

;; (use-package buttercup)

;;;; Utilities

(defmacro with-fontified-clojure-ts-buffer (content &rest body)
"Evaluate BODY in a temporary buffer with CONTENT."
(declare (debug t)
(indent 1))
`(with-clojure-ts-buffer ,content
(font-lock-ensure)
(goto-char (point-min))
,@body))

(defun clojure-ts-get-face-at (start end content)
"Get the face between START and END in CONTENT."
(with-fontified-clojure-ts-buffer content
(let ((start-face (get-text-property start 'face))
(all-faces (cl-loop for i from start to end collect (get-text-property
i 'face))))
(if (cl-every (lambda (face) (eq face start-face)) all-faces)
start-face
'various-faces))))

(defun expect-face-at (content start end face)
"Expect face in CONTENT between START and END to be equal to FACE."
(expect (clojure-ts-get-face-at start end content) :to-equal face))

(defun expect-faces-at (content &rest faces)
"Expect FACES in CONTENT.

FACES is a list of the form (content (start end expected-face)*)"
(dolist (face faces)
(apply (apply-partially #'expect-face-at content) face)))

(defmacro when-fontifying-it (description &rest tests)
"Return a buttercup spec.

TESTS are lists of the form (content (start end expected-face)*). For each test
check that each `expected-face` is found in `content` between `start` and `end`.

DESCRIPTION is the description of the spec."
(declare (indent 1))
`(it ,description
(dolist (test (quote ,tests))
(apply #'expect-faces-at test))))

;;;; Font locking

(describe "clojure-ts-mode-syntax-table"
(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))


;; TODO: copied from clojure-mode, but failing
;; ("(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 "variable-def-string-with-docstring"
("(def foo \"usage\" \"hello\")"
(10 16 font-lock-doc-face)
(18 24 font-lock-string-face))

("(def foo \"usage\" \"hello\" )"
(18 24 font-lock-string-face))

("(def foo \"usage\" \n \"hello\")"
(21 27 font-lock-string-face))

("(def foo \n \"usage\" \"hello\")"
(13 19 font-lock-doc-face))

("(def foo \n \"usage\" \n \"hello\")"
(13 19 font-lock-doc-face)
(24 30 font-lock-string-face))

("(def test-string\n \"this\\n\n is\n my\n string\")"
(20 24 font-lock-string-face)
(25 26 font-lock-string-face)
(27 46 font-lock-string-face)))

(when-fontifying-it "variable-def-with-metadata-and-docstring"
("^{:foo bar}(def foo \n \"usage\" \n \"hello\")"
(13 15 font-lock-keyword-face)
(17 19 font-lock-variable-name-face)
(24 30 font-lock-doc-face)
(35 41 font-lock-string-face)))

(when-fontifying-it "defn-with-metadata-and-docstring"
("^{:foo bar}(defn foo \n \"usage\" \n [] \n \"hello\")"
(13 16 font-lock-keyword-face)
(18 20 font-lock-function-name-face)
(25 31 font-lock-doc-face)
(40 46 font-lock-string-face))))
2 changes: 1 addition & 1 deletion test/clojure-ts-mode-imenu-test.el
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
;;; clojure-ts-mode-util-test.el --- Clojure TS Mode: util test suite -*- lexical-binding: t; -*-
;;; clojure-ts-mode-imenu-test.el --- Clojure TS Mode: imenu test suite -*- lexical-binding: t; -*-

;; Copyright © 2022-2024 Danny Freeman

Expand Down
3 changes: 3 additions & 0 deletions test/samples/test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,9 @@ clojure.core/map

(def ^Integer x 1)

^{:foo true}
(defn b "hello" [] "world")

(comment
(defrecord TestRecord [field]
AutoCloseable
Expand Down