branch: elpa/clojure-ts-mode commit ec48877dd0394d176dd08334ee48d1ff48643453 Author: Danny Freeman <danny@dfreeman.email> Commit: Danny Freeman <danny@dfreeman.email>
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. --- clojure-ts-mode.el | 72 +++++++++++++++++++++++++++++++++-------------------- test/docstrings.clj | 67 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 112 insertions(+), 27 deletions(-) diff --git a/clojure-ts-mode.el b/clojure-ts-mode.el index 224f3b95b1..299fa42bdf 100644 --- a/clojure-ts-mode.el +++ b/clojure-ts-mode.el @@ -202,17 +202,26 @@ Only intended for use at development time.") '((t (:inherit font-lock-string-face))) "Face used to font-lock Clojure character literals.") -(defconst clojure-ts--definition-symbol-regexp - (rx - line-start - (or (group "fn") - (group "def" - (+ (or alnum - ;; What are valid characters for symbols? - ;; is a negative match better? - "-" "_" "!" "@" "#" "$" "%" "^" "&" - "*" "|" "?" "<" ">" "+" "=" ":")))) - line-end)) +(defun clojure-ts-symbol-regexp (symbols) + "Return a regular expression that matches one of SYMBOLS exactly." + (concat "^" (regexp-opt symbols) "$")) + +(defvar clojure-ts-function-docstring-symbols + '("definline" + "defmulti" + "defmacro" + "defn" + "defn-" + "defprotocol" + "ns") + "Symbols that accept an optional docstring as their second argument.") + +(defvar clojure-ts-definition-docstring-symbols + '("def") + "Symbols that accept an optional docstring as their second argument. +Any symbols added here should only treat their second argument as a docstring +if a third argument (the value) is provided. +\"def\" is the only builtin Clojure symbol that behaves like this.") (defconst clojure-ts--variable-definition-symbol-regexp (eval-and-compile @@ -244,40 +253,49 @@ Only intended for use at development time.") (defun clojure-ts--docstring-query (capture-symbol) "Return a query that captures docstrings with CAPTURE-SYMBOL." - `(;; Captures docstrings in def, defonce - ((list_lit :anchor (sym_lit) @def_symbol + `(;; Captures docstrings in def + ((list_lit :anchor (sym_lit) @_def_symbol + :anchor (comment) :? :anchor (sym_lit) ; variable name + :anchor (comment) :? :anchor (str_lit) ,capture-symbol :anchor (_)) ; the variable's value - (:match ,clojure-ts--variable-definition-symbol-regexp @def_symbol)) + (:match ,(clojure-ts-symbol-regexp clojure-ts-definition-docstring-symbols) + @_def_symbol)) ;; Captures docstrings in metadata of definitions - ((list_lit :anchor (sym_lit) @def_symbol + ((list_lit :anchor (sym_lit) @_def_symbol + :anchor (comment) :? :anchor (sym_lit (meta_lit value: (map_lit - (kwd_lit) @doc-keyword + (kwd_lit) @_doc-keyword :anchor (str_lit) ,capture-symbol)))) ;; We're only supporting this on a fixed set of defining symbols ;; Existing regexes don't encompass def and defn ;; Naming another regex is very cumbersome. - (:match ,(regexp-opt '("def" "defonce" "defn" "defn-" "defmacro" "ns" - "defmulti" "definterface" "defprotocol" - "deftype" "defrecord" "defstruct")) - @def_symbol) - (:equal @doc-keyword ":doc")) + (:match ,(clojure-ts-symbol-regexp + '("def" "defonce" "defn" "defn-" "defmacro" "ns" + "defmulti" "definterface" "defprotocol" + "deftest" "deftest-" + "deftype" "defrecord" "defstruct")) + @_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 (sym_lit) @_def_symbol + :anchor (comment) :? :anchor (sym_lit) ; function_name + :anchor (comment) :? :anchor (str_lit) ,capture-symbol) - (:match ,clojure-ts--definition-symbol-regexp @def_symbol)) + (:match ,(clojure-ts-symbol-regexp clojure-ts-function-docstring-symbols) + @_def_symbol)) ;; Captures docstrings in defprotcol, definterface - ((list_lit :anchor (sym_lit) @def_symbol + ((list_lit :anchor (sym_lit) @_def_symbol (list_lit :anchor (sym_lit) (vec_lit) :* (str_lit) ,capture-symbol :anchor) :*) - (:match ,clojure-ts--interface-def-symbol-regexp @def_symbol)))) + (:match ,clojure-ts--interface-def-symbol-regexp @_def_symbol)))) (defvar clojure-ts--treesit-range-settings (treesit-range-rules @@ -752,7 +770,7 @@ forms like deftype, defrecord, reify, proxy, etc." (and (treesit-node-eq node (treesit-node-child parent 2 t)) (let ((first-auncle (treesit-node-child parent 0 t))) (clojure-ts--symbol-matches-p - clojure-ts--definition-symbol-regexp + (regexp-opt clojure-ts-function-docstring-symbols) first-auncle))))) (defun clojure-ts--match-def-docstring (node) @@ -765,7 +783,7 @@ forms like deftype, defrecord, reify, proxy, etc." (treesit-node-child parent 3 t) (let ((first-auncle (treesit-node-child parent 0 t))) (clojure-ts--symbol-matches-p - clojure-ts--variable-definition-symbol-regexp + (regexp-opt clojure-ts-definition-docstring-symbols) first-auncle))))) (defun clojure-ts--match-method-docstring (node) diff --git a/test/docstrings.clj b/test/docstrings.clj new file mode 100644 index 0000000000..c3bb2a773b --- /dev/null +++ b/test/docstrings.clj @@ -0,0 +1,67 @@ +(ns clojure-ts-mode.docstrings + "This is a namespace + See my famous `fix-bug` macro if you need help." + (:require [clojure.test :refer [deftest]]) + (:import (java.util UUID))) + +(def foo ;;asdf + "I'm a value") +(def bar "I'm a docstring" "and I'm a value") + +(defonce ^{:doc "gotta document in metadata."} baz + "Did you know defonce doesn't have a docstring arity like def?") + +(def foobar + ;; Comments shouldn't disrupt docstring highlighting + "I'm a docstring" + 123) + +(defn ;;asdf + foobarbaz ;;asdf + "I'm the docstring!" ;;asdf + [x] + (inc x)) + +(;; starting comments break docstrings + defn busted! + "We really need to anchor symbols like defn to the front of the list. +I don't want every query to have to check for comments. +Don't format code this way." + [] + nil) + +(defn buzz "Looking for `fizz`" + [x] + (when (zero? (% x 5)) + "buzz")) + +(defn- fizz + "Pairs well with `buzz`" + [x] + (when (zero? (% x 3)) + "fizz")) + +(defmacro fix-bug + "Fixes most known bugs." + [& body] + `(try + ~@body + (catch Throwable _ + nil))) + +(definline never-used-this ":)" [x] x) + +(deftype ^{:doc "asdf" :something-else "asdf"} T + java.lang.Closeable + (close [this] + (print "done"))) + +(defprotocol Fooable + (foo [this] + "Does foo")) + +(definterface Barable + (^String bar [] "Does bar")) + +(deftest ^{:doc "doctest"} some-test + (is (= 1 2)))