From 89e0e71903e9ec3b69ed8916482add5e243b53a0 Mon Sep 17 00:00:00 2001 From: Dieter Komendera Date: Fri, 25 Oct 2024 23:14:00 +0200 Subject: [PATCH 1/5] Fix font locking of defintions with metadata --- CHANGELOG.md | 1 + clojure-ts-mode.el | 14 +++++++++----- test/samples/test.clj | 3 +++ 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 743d6b7..17e5c96 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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) diff --git a/clojure-ts-mode.el b/clojure-ts-mode.el index a562f03..a538b67 100644 --- a/clojure-ts-mode.el +++ b/clojure-ts-mode.el @@ -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) :? @@ -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) :? @@ -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))) @@ -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 @@ -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))) diff --git a/test/samples/test.clj b/test/samples/test.clj index ce4c029..1ab5efa 100644 --- a/test/samples/test.clj +++ b/test/samples/test.clj @@ -289,6 +289,9 @@ clojure.core/map (def ^Integer x 1) +^{:foo true} +(defn b "hello" [] "world") + (comment (defrecord TestRecord [field] AutoCloseable From 2f1a02ed60bbedfded2acac2b5066bcf6370c9c7 Mon Sep 17 00:00:00 2001 From: Dieter Komendera Date: Sat, 26 Oct 2024 12:35:36 +0200 Subject: [PATCH 2/5] Fix comment --- test/clojure-ts-mode-imenu-test.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/clojure-ts-mode-imenu-test.el b/test/clojure-ts-mode-imenu-test.el index 1dbe71c..ede2747 100644 --- a/test/clojure-ts-mode-imenu-test.el +++ b/test/clojure-ts-mode-imenu-test.el @@ -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 From aa7d2857b0d61736c401aef4b796e1cccdc60583 Mon Sep 17 00:00:00 2001 From: Dieter Komendera Date: Sat, 26 Oct 2024 12:35:55 +0200 Subject: [PATCH 3/5] Port font lock test infrastructure and some basic tests over from clojure-mode --- test/clojure-ts-mode-font-lock-test.el | 101 +++++++++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 test/clojure-ts-mode-font-lock-test.el diff --git a/test/clojure-ts-mode-font-lock-test.el b/test/clojure-ts-mode-font-lock-test.el new file mode 100644 index 0000000..eb90995 --- /dev/null +++ b/test/clojure-ts-mode-font-lock-test.el @@ -0,0 +1,101 @@ +;;; 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 . + +;;; 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))) + ) From dcf87d237a1f40dbd1c835e79df43fbdf950a6c0 Mon Sep 17 00:00:00 2001 From: Dieter Komendera Date: Sat, 26 Oct 2024 13:17:06 +0200 Subject: [PATCH 4/5] Port variable-def-string-with-docstring font lock test from clojure-mode --- test/clojure-ts-mode-font-lock-test.el | 46 +++++++++++++++++++------- 1 file changed, 34 insertions(+), 12 deletions(-) diff --git a/test/clojure-ts-mode-font-lock-test.el b/test/clojure-ts-mode-font-lock-test.el index eb90995..3c904f0 100644 --- a/test/clojure-ts-mode-font-lock-test.el +++ b/test/clojure-ts-mode-font-lock-test.el @@ -87,15 +87,37 @@ DESCRIPTION is the description of the spec." ("(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))) - ) + ("(deftest a (is (= 1 1)))" (2 8 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))) + + ;; 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)) +) From 2267151a1aa61b632c38f7448fd485be86458648 Mon Sep 17 00:00:00 2001 From: Dieter Komendera Date: Sat, 26 Oct 2024 14:11:25 +0200 Subject: [PATCH 5/5] Add font lock tests for def and defn with metadata --- test/clojure-ts-mode-font-lock-test.el | 36 +++++++++++++++++++------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/test/clojure-ts-mode-font-lock-test.el b/test/clojure-ts-mode-font-lock-test.el index 3c904f0..f966652 100644 --- a/test/clojure-ts-mode-font-lock-test.el +++ b/test/clojure-ts-mode-font-lock-test.el @@ -87,7 +87,19 @@ DESCRIPTION is the description of the spec." ("(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))) + ("(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\")" @@ -112,12 +124,16 @@ DESCRIPTION is the description of the spec." (25 26 font-lock-string-face) (27 46 font-lock-string-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-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))))