Skip to content

Commit ec48877

Browse files
committed
Fix ns docstring highlighting regression
This also begins the process of making our symbol matching regular expressions user extensible (see issue #15). There are more to convert from regexps to normal lists, but I want to take my time and make sure I get the names down correctly. It is important to get maximum reuse so users don't have to add their fancy def-whatever to 4 different lists.
1 parent 6ea4196 commit ec48877

File tree

2 files changed

+112
-27
lines changed

2 files changed

+112
-27
lines changed

clojure-ts-mode.el

+45-27
Original file line numberDiff line numberDiff line change
@@ -202,17 +202,26 @@ Only intended for use at development time.")
202202
'((t (:inherit font-lock-string-face)))
203203
"Face used to font-lock Clojure character literals.")
204204

205-
(defconst clojure-ts--definition-symbol-regexp
206-
(rx
207-
line-start
208-
(or (group "fn")
209-
(group "def"
210-
(+ (or alnum
211-
;; What are valid characters for symbols?
212-
;; is a negative match better?
213-
"-" "_" "!" "@" "#" "$" "%" "^" "&"
214-
"*" "|" "?" "<" ">" "+" "=" ":"))))
215-
line-end))
205+
(defun clojure-ts-symbol-regexp (symbols)
206+
"Return a regular expression that matches one of SYMBOLS exactly."
207+
(concat "^" (regexp-opt symbols) "$"))
208+
209+
(defvar clojure-ts-function-docstring-symbols
210+
'("definline"
211+
"defmulti"
212+
"defmacro"
213+
"defn"
214+
"defn-"
215+
"defprotocol"
216+
"ns")
217+
"Symbols that accept an optional docstring as their second argument.")
218+
219+
(defvar clojure-ts-definition-docstring-symbols
220+
'("def")
221+
"Symbols that accept an optional docstring as their second argument.
222+
Any symbols added here should only treat their second argument as a docstring
223+
if a third argument (the value) is provided.
224+
\"def\" is the only builtin Clojure symbol that behaves like this.")
216225

217226
(defconst clojure-ts--variable-definition-symbol-regexp
218227
(eval-and-compile
@@ -244,40 +253,49 @@ Only intended for use at development time.")
244253

245254
(defun clojure-ts--docstring-query (capture-symbol)
246255
"Return a query that captures docstrings with CAPTURE-SYMBOL."
247-
`(;; Captures docstrings in def, defonce
248-
((list_lit :anchor (sym_lit) @def_symbol
256+
`(;; Captures docstrings in def
257+
((list_lit :anchor (sym_lit) @_def_symbol
258+
:anchor (comment) :?
249259
:anchor (sym_lit) ; variable name
260+
:anchor (comment) :?
250261
:anchor (str_lit) ,capture-symbol
251262
:anchor (_)) ; the variable's value
252-
(:match ,clojure-ts--variable-definition-symbol-regexp @def_symbol))
263+
(:match ,(clojure-ts-symbol-regexp clojure-ts-definition-docstring-symbols)
264+
@_def_symbol))
253265
;; Captures docstrings in metadata of definitions
254-
((list_lit :anchor (sym_lit) @def_symbol
266+
((list_lit :anchor (sym_lit) @_def_symbol
267+
:anchor (comment) :?
255268
:anchor (sym_lit
256269
(meta_lit
257270
value: (map_lit
258-
(kwd_lit) @doc-keyword
271+
(kwd_lit) @_doc-keyword
259272
:anchor
260273
(str_lit) ,capture-symbol))))
261274
;; We're only supporting this on a fixed set of defining symbols
262275
;; Existing regexes don't encompass def and defn
263276
;; Naming another regex is very cumbersome.
264-
(:match ,(regexp-opt '("def" "defonce" "defn" "defn-" "defmacro" "ns"
265-
"defmulti" "definterface" "defprotocol"
266-
"deftype" "defrecord" "defstruct"))
267-
@def_symbol)
268-
(:equal @doc-keyword ":doc"))
277+
(:match ,(clojure-ts-symbol-regexp
278+
'("def" "defonce" "defn" "defn-" "defmacro" "ns"
279+
"defmulti" "definterface" "defprotocol"
280+
"deftest" "deftest-"
281+
"deftype" "defrecord" "defstruct"))
282+
@_def_symbol)
283+
(:equal @_doc-keyword ":doc"))
269284
;; Captures docstrings defn, defmacro, ns, and things like that
270-
((list_lit :anchor (sym_lit) @def_symbol
285+
((list_lit :anchor (sym_lit) @_def_symbol
286+
:anchor (comment) :?
271287
:anchor (sym_lit) ; function_name
288+
:anchor (comment) :?
272289
:anchor (str_lit) ,capture-symbol)
273-
(:match ,clojure-ts--definition-symbol-regexp @def_symbol))
290+
(:match ,(clojure-ts-symbol-regexp clojure-ts-function-docstring-symbols)
291+
@_def_symbol))
274292
;; Captures docstrings in defprotcol, definterface
275-
((list_lit :anchor (sym_lit) @def_symbol
293+
((list_lit :anchor (sym_lit) @_def_symbol
276294
(list_lit
277295
:anchor (sym_lit) (vec_lit) :*
278296
(str_lit) ,capture-symbol :anchor)
279297
:*)
280-
(:match ,clojure-ts--interface-def-symbol-regexp @def_symbol))))
298+
(:match ,clojure-ts--interface-def-symbol-regexp @_def_symbol))))
281299

282300
(defvar clojure-ts--treesit-range-settings
283301
(treesit-range-rules
@@ -752,7 +770,7 @@ forms like deftype, defrecord, reify, proxy, etc."
752770
(and (treesit-node-eq node (treesit-node-child parent 2 t))
753771
(let ((first-auncle (treesit-node-child parent 0 t)))
754772
(clojure-ts--symbol-matches-p
755-
clojure-ts--definition-symbol-regexp
773+
(regexp-opt clojure-ts-function-docstring-symbols)
756774
first-auncle)))))
757775

758776
(defun clojure-ts--match-def-docstring (node)
@@ -765,7 +783,7 @@ forms like deftype, defrecord, reify, proxy, etc."
765783
(treesit-node-child parent 3 t)
766784
(let ((first-auncle (treesit-node-child parent 0 t)))
767785
(clojure-ts--symbol-matches-p
768-
clojure-ts--variable-definition-symbol-regexp
786+
(regexp-opt clojure-ts-definition-docstring-symbols)
769787
first-auncle)))))
770788

771789
(defun clojure-ts--match-method-docstring (node)

test/docstrings.clj

+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
(ns clojure-ts-mode.docstrings
2+
"This is a namespace
3+
See my famous `fix-bug` macro if you need help."
4+
(:require [clojure.test :refer [deftest]])
5+
(:import (java.util UUID)))
6+
7+
(def foo ;;asdf
8+
"I'm a value")
9+
(def bar "I'm a docstring" "and I'm a value")
10+
11+
(defonce ^{:doc "gotta document in metadata."} baz
12+
"Did you know defonce doesn't have a docstring arity like def?")
13+
14+
(def foobar
15+
;; Comments shouldn't disrupt docstring highlighting
16+
"I'm a docstring"
17+
123)
18+
19+
(defn ;;asdf
20+
foobarbaz ;;asdf
21+
"I'm the docstring!" ;;asdf
22+
[x]
23+
(inc x))
24+
25+
(;; starting comments break docstrings
26+
defn busted!
27+
"We really need to anchor symbols like defn to the front of the list.
28+
I don't want every query to have to check for comments.
29+
Don't format code this way."
30+
[]
31+
nil)
32+
33+
(defn buzz "Looking for `fizz`"
34+
[x]
35+
(when (zero? (% x 5))
36+
"buzz"))
37+
38+
(defn- fizz
39+
"Pairs well with `buzz`"
40+
[x]
41+
(when (zero? (% x 3))
42+
"fizz"))
43+
44+
(defmacro fix-bug
45+
"Fixes most known bugs."
46+
[& body]
47+
`(try
48+
~@body
49+
(catch Throwable _
50+
nil)))
51+
52+
(definline never-used-this ":)" [x] x)
53+
54+
(deftype ^{:doc "asdf" :something-else "asdf"} T
55+
java.lang.Closeable
56+
(close [this]
57+
(print "done")))
58+
59+
(defprotocol Fooable
60+
(foo [this]
61+
"Does foo"))
62+
63+
(definterface Barable
64+
(^String bar [] "Does bar"))
65+
66+
(deftest ^{:doc "doctest"} some-test
67+
(is (= 1 2)))

0 commit comments

Comments
 (0)