Skip to content

Commit 7d38baf

Browse files
committed
[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.
1 parent 6854495 commit 7d38baf

3 files changed

Lines changed: 56 additions & 24 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
### Bugs fixed
1919

20+
* [#402](https://github.com/clojure-emacs/clojure-mode/issues/402): Font-lock protocol method docstrings with `font-lock-doc-face`.
2021
* Fix typos in `clojure-mode-extra-font-locking`: `halt-when?` -> `halt-when`, `simple-indent?` -> `simple-ident?`.
2122
* Fix `doc` and `find-doc` misplaced under `clojure.core` instead of `clojure.repl` in extra font-locking.
2223

clojure-mode.el

Lines changed: 38 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1213,30 +1213,44 @@ highlighted region)."
12131213
(docelt (and firstsym
12141214
(function-get (intern-soft firstsym)
12151215
lisp-doc-string-elt-property))))
1216-
(if (and docelt
1217-
;; It's a string in a form that can have a docstring.
1218-
;; Check whether it's in docstring position.
1219-
(save-excursion
1220-
(when (functionp docelt)
1221-
(goto-char (match-end 1))
1222-
(setq docelt (funcall docelt)))
1223-
(goto-char listbeg)
1224-
(forward-char 1)
1225-
(ignore-errors
1226-
(while (and (> docelt 0) (< (point) startpos)
1227-
(progn (forward-sexp 1) t))
1228-
;; ignore metadata and type hints
1229-
(unless (looking-at "[ \n\t]*\\(\\^[A-Z:].+\\|\\^?{.+\\)")
1230-
(setq docelt (1- docelt)))))
1231-
(and (zerop docelt) (<= (point) startpos)
1232-
(progn (forward-comment (point-max)) t)
1233-
(= (point) (nth 8 state))))
1234-
;; In a def, at last position is not a docstring
1235-
(not (and (string= "def" firstsym)
1236-
(save-excursion
1237-
(goto-char startpos)
1238-
(goto-char (end-of-thing 'sexp))
1239-
(looking-at "[ \r\n\t]*\)")))))
1216+
(if (or (and docelt
1217+
;; It's a string in a form that can have a docstring.
1218+
;; Check whether it's in docstring position.
1219+
(save-excursion
1220+
(when (functionp docelt)
1221+
(goto-char (match-end 1))
1222+
(setq docelt (funcall docelt)))
1223+
(goto-char listbeg)
1224+
(forward-char 1)
1225+
(ignore-errors
1226+
(while (and (> docelt 0) (< (point) startpos)
1227+
(progn (forward-sexp 1) t))
1228+
;; ignore metadata and type hints
1229+
(unless (looking-at "[ \n\t]*\\(\\^[A-Z:].+\\|\\^?{.+\\)")
1230+
(setq docelt (1- docelt)))))
1231+
(and (zerop docelt) (<= (point) startpos)
1232+
(progn (forward-comment (point-max)) t)
1233+
(= (point) (nth 8 state))))
1234+
;; In a def, at last position is not a docstring
1235+
(not (and (string= "def" firstsym)
1236+
(save-excursion
1237+
(goto-char startpos)
1238+
(goto-char (end-of-thing 'sexp))
1239+
(looking-at "[ \r\n\t]*\)")))))
1240+
;; Protocol method docstring: string is last in the
1241+
;; method form and parent form is defprotocol.
1242+
(and listbeg
1243+
(save-excursion
1244+
(goto-char startpos)
1245+
(ignore-errors (forward-sexp))
1246+
(skip-chars-forward " \t\n\r")
1247+
(eq (char-after) ?\)))
1248+
(save-excursion
1249+
(let ((parent-beg (nth 1 (parse-partial-sexp
1250+
(point-min) listbeg))))
1251+
(and parent-beg
1252+
(goto-char parent-beg)
1253+
(looking-at "([ \t\n]*defprotocol\\>"))))))
12401254
font-lock-doc-face
12411255
font-lock-string-face))
12421256
font-lock-comment-face))

test/clojure-mode-font-lock-test.el

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1043,6 +1043,23 @@ DESCRIPTION is the description of the spec."
10431043
("*some-var?*"
10441044
(1 11 font-lock-variable-name-face))))
10451045

1046+
(describe "docstring font-locking"
1047+
(it "should font-lock defn docstrings"
1048+
(expect-face-at "(defn foo\n \"docstring\"\n [x] x)" 14 22 font-lock-doc-face))
1049+
1050+
(it "should font-lock defprotocol docstrings"
1051+
(expect-face-at "(defprotocol Foo\n \"protocol doc\")" 21 32 font-lock-doc-face))
1052+
1053+
(it "should font-lock protocol method docstrings"
1054+
(expect-face-at "(defprotocol Foo\n (bar [this]\n \"method doc\"))" 37 46 font-lock-doc-face))
1055+
1056+
(it "should font-lock protocol method docstrings with multiple arities"
1057+
(expect-face-at "(defprotocol Foo\n (bar [this] [this x]\n \"method doc\"))" 46 55 font-lock-doc-face))
1058+
1059+
(it "should not font-lock regular strings in protocol methods as docstrings"
1060+
(expect-face-at "(defprotocol Foo\n (bar [this]\n \"not a doc\" \"method doc\"))"
1061+
37 45 font-lock-string-face)))
1062+
10461063
(provide 'clojure-mode-font-lock-test)
10471064

10481065
;;; clojure-mode-font-lock-test.el ends here

0 commit comments

Comments
 (0)