branch: elpa/clojure-mode
commit 7d38bafd263ea07d56853948e13af0f847635eac
Author: Bozhidar Batsov <[email protected]>
Commit: Bozhidar Batsov <[email protected]>

    [Fix #402] Font-lock protocol method docstrings
    
    Protocol method docstrings were rendered as regular strings instead of
    with font-lock-doc-face. Detect them by checking if the string is the
    last element in a method form inside a defprotocol.
---
 CHANGELOG.md                        |  1 +
 clojure-mode.el                     | 62 +++++++++++++++++++++++--------------
 test/clojure-mode-font-lock-test.el | 17 ++++++++++
 3 files changed, 56 insertions(+), 24 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 24cb6fcf71..daf08b3b07 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -17,6 +17,7 @@
 
 ### Bugs fixed
 
+* [#402](https://github.com/clojure-emacs/clojure-mode/issues/402): Font-lock 
protocol method docstrings with `font-lock-doc-face`.
 * Fix typos in `clojure-mode-extra-font-locking`: `halt-when?` -> `halt-when`, 
`simple-indent?` -> `simple-ident?`.
 * Fix `doc` and `find-doc` misplaced under `clojure.core` instead of 
`clojure.repl` in extra font-locking.
 
diff --git a/clojure-mode.el b/clojure-mode.el
index 27347286b9..7ff2a16d3f 100644
--- a/clojure-mode.el
+++ b/clojure-mode.el
@@ -1213,30 +1213,44 @@ highlighted region)."
              (docelt (and firstsym
                           (function-get (intern-soft firstsym)
                                         lisp-doc-string-elt-property))))
-        (if (and docelt
-                 ;; It's a string in a form that can have a docstring.
-                 ;; Check whether it's in docstring position.
-                 (save-excursion
-                   (when (functionp docelt)
-                     (goto-char (match-end 1))
-                     (setq docelt (funcall docelt)))
-                   (goto-char listbeg)
-                   (forward-char 1)
-                   (ignore-errors
-                     (while (and (> docelt 0) (< (point) startpos)
-                                 (progn (forward-sexp 1) t))
-                       ;; ignore metadata and type hints
-                       (unless (looking-at "[ 
\n\t]*\\(\\^[A-Z:].+\\|\\^?{.+\\)")
-                         (setq docelt (1- docelt)))))
-                   (and (zerop docelt) (<= (point) startpos)
-                        (progn (forward-comment (point-max)) t)
-                        (= (point) (nth 8 state))))
-                 ;; In a def, at last position is not a docstring
-                 (not (and (string= "def" firstsym)
-                           (save-excursion
-                             (goto-char startpos)
-                             (goto-char (end-of-thing 'sexp))
-                             (looking-at "[ \r\n\t]*\)")))))
+        (if (or (and docelt
+                     ;; It's a string in a form that can have a docstring.
+                     ;; Check whether it's in docstring position.
+                     (save-excursion
+                       (when (functionp docelt)
+                         (goto-char (match-end 1))
+                         (setq docelt (funcall docelt)))
+                       (goto-char listbeg)
+                       (forward-char 1)
+                       (ignore-errors
+                         (while (and (> docelt 0) (< (point) startpos)
+                                     (progn (forward-sexp 1) t))
+                           ;; ignore metadata and type hints
+                           (unless (looking-at "[ 
\n\t]*\\(\\^[A-Z:].+\\|\\^?{.+\\)")
+                             (setq docelt (1- docelt)))))
+                       (and (zerop docelt) (<= (point) startpos)
+                            (progn (forward-comment (point-max)) t)
+                            (= (point) (nth 8 state))))
+                     ;; In a def, at last position is not a docstring
+                     (not (and (string= "def" firstsym)
+                               (save-excursion
+                                 (goto-char startpos)
+                                 (goto-char (end-of-thing 'sexp))
+                                 (looking-at "[ \r\n\t]*\)")))))
+                ;; Protocol method docstring: string is last in the
+                ;; method form and parent form is defprotocol.
+                (and listbeg
+                     (save-excursion
+                       (goto-char startpos)
+                       (ignore-errors (forward-sexp))
+                       (skip-chars-forward " \t\n\r")
+                       (eq (char-after) ?\)))
+                     (save-excursion
+                       (let ((parent-beg (nth 1 (parse-partial-sexp
+                                                 (point-min) listbeg))))
+                         (and parent-beg
+                              (goto-char parent-beg)
+                              (looking-at "([ \t\n]*defprotocol\\>"))))))
             font-lock-doc-face
           font-lock-string-face))
     font-lock-comment-face))
diff --git a/test/clojure-mode-font-lock-test.el 
b/test/clojure-mode-font-lock-test.el
index b7eab44aa0..3b4eb1aa29 100644
--- a/test/clojure-mode-font-lock-test.el
+++ b/test/clojure-mode-font-lock-test.el
@@ -1043,6 +1043,23 @@ DESCRIPTION is the description of the spec."
     ("*some-var?*"
      (1 11 font-lock-variable-name-face))))
 
+(describe "docstring font-locking"
+  (it "should font-lock defn docstrings"
+    (expect-face-at "(defn foo\n  \"docstring\"\n  [x] x)" 14 22 
font-lock-doc-face))
+
+  (it "should font-lock defprotocol docstrings"
+    (expect-face-at "(defprotocol Foo\n  \"protocol doc\")" 21 32 
font-lock-doc-face))
+
+  (it "should font-lock protocol method docstrings"
+    (expect-face-at "(defprotocol Foo\n  (bar [this]\n    \"method doc\"))" 37 
46 font-lock-doc-face))
+
+  (it "should font-lock protocol method docstrings with multiple arities"
+    (expect-face-at "(defprotocol Foo\n  (bar [this] [this x]\n    \"method 
doc\"))" 46 55 font-lock-doc-face))
+
+  (it "should not font-lock regular strings in protocol methods as docstrings"
+    (expect-face-at "(defprotocol Foo\n  (bar [this]\n    \"not a doc\" 
\"method doc\"))"
+                    37 45 font-lock-string-face)))
+
 (provide 'clojure-mode-font-lock-test)
 
 ;;; clojure-mode-font-lock-test.el ends here

Reply via email to