emacs-elpa-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[nongnu] elpa/clojure-ts-mode 9af0a6b35c 1/2: Use nested markdown gramma


From: ELPA Syncer
Subject: [nongnu] elpa/clojure-ts-mode 9af0a6b35c 1/2: Use nested markdown grammar for highlighting docstrings
Date: Sat, 9 Sep 2023 12:59:34 -0400 (EDT)

branch: elpa/clojure-ts-mode
commit 9af0a6b35c708309acdfeb4c0c79061b0fd4eb44
Author: Danny Freeman <danny@dfreeman.email>
Commit: Danny Freeman <danny@dfreeman.email>

    Use nested markdown grammar for highlighting docstrings
    
    This uses the "inline" version of the markdown grammar.
    
    https://github.com/MDeiml/tree-sitter-markdown
    
    see issue #18
---
 CHANGELOG.md         |   3 +
 README.md            |  13 +-
 clojure-ts-mode.el   | 435 +++++++++++++++++++++++++++++----------------------
 test/indentation.clj |   4 +-
 4 files changed, 260 insertions(+), 195 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index b45192d0b0..59fedbda22 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,9 @@
    - (See treesit-font-lock-level documentation for more information.)
 - Highlight docstrings in interface, protocol, and variable definitions
 - Add support for semantic indentation (now the default)
+- Highlight "\`quoted-symbols\`  in docs strings like this."
+   - This feature uses a nested markdown parser.
+     If the parser is not available this feature should be silently disabled.
 
 ## 0.1.5
 
diff --git a/README.md b/README.md
index dde2547a13..9dc5438ff8 100644
--- a/README.md
+++ b/README.md
@@ -106,11 +106,14 @@ git clone 
https://github.com/clojure-emacs/clojure-ts-mode.git
 
 Once installed, evaluate clojure-ts-mode.el and you should be ready to go.
 
-### Install libtree-sitter-clojure shared library
+### Install tree-sitter grammars
+
+The compile tree-sitter clojure shared library must be available to Emacs.
+Additionally, the tree-sitter 
[markdown_inline](https://github.com/MDeiml/tree-sitter-markdown) shared 
library will also be used for docstrings if available.
 
-The tree-sitter clojure shared library must be available to Emacs.
 If you have `git` and a C compiler (`cc`) available on your system's `PATH`, 
**then these steps should not be necessary**.
-clojure-ts-mode will install the grammar when you first open a Clojure file.
+clojure-ts-mode will install the grammars when you first open a Clojure file 
and
+`clojure-ts-ensure-grammars` is set to `t` (the default).
 
 If clojure-ts-mode fails to automatically install the grammar, you have the 
option to install it manually.
 
@@ -120,7 +123,9 @@ Some distributions may package the tree-sitter-clojure 
grammar in their package
 If yours does you may be able to install tree-sitter-clojure with your system 
package manager.
 
 If the version packaged by your OS is out of date, you may see errors in the 
`*Messages*` buffer or your clojure buffers will not have any syntax 
highlighting.
-If this happens you should install the grammar manually with `M-x 
treesit-install-language-grammar <RET> clojure`.
+
+If this happens you should install the grammar manually with `M-x 
treesit-install-language-grammar <RET> clojure` and follow the prompts.
+Recommended values for these prompts can be seen in 
`clojure-ts-grammar-recipes`.
 
 #### Compile From Source
 
diff --git a/clojure-ts-mode.el b/clojure-ts-mode.el
index b2554e82bb..eb267b7a25 100644
--- a/clojure-ts-mode.el
+++ b/clojure-ts-mode.el
@@ -86,6 +86,12 @@ itself."
   :type 'boolean
   :package-version '(clojure-ts-mode . "0.1.3"))
 
+(defcustom clojure-ts-ensure-grammars t
+  "When non-nil, ensure required tree-sitter grammars are installed."
+  :safe #'booleanp
+  :type 'boolean
+  :package-version '(clojure-ts-mode . "0.2.0"))
+
 (defvar clojure-ts--debug nil
   "Enables debugging messages, shows current node in mode-line.
 Only intended for use at development time.")
@@ -233,167 +239,188 @@ Only intended for use at development time.")
     (rx line-start (or "defprotocol" "definterface") line-end))
   "A regular expression matching a symbol used to define an interface.")
 
-(defun clojure-ts--font-lock-settings ()
-  "Return font lock settings suitable for use in `treesit-font-lock-settings'."
-  (treesit-font-lock-rules
-   :feature 'string
-   :language 'clojure
-   '((str_lit) @font-lock-string-face
-     (regex_lit) @font-lock-regexp-face)
-
-   :feature 'regex
-   :language 'clojure
-   :override t
-   '((regex_lit marker: _ @font-lock-property-face))
-
-   :feature 'number
-   :language 'clojure
-   '((num_lit) @font-lock-number-face)
-
-   :feature 'constant
-   :language 'clojure
-   '([(bool_lit) (nil_lit)] @font-lock-constant-face)
-
-   :feature 'char
-   :language 'clojure
-   '((char_lit) @clojure-ts-character-face)
-
-   :feature 'keyword
-   :language 'clojure
-   '((kwd_ns) @font-lock-type-face
-     (kwd_name) @clojure-ts-keyword-face
-     (kwd_lit
-      marker: _ @clojure-ts-keyword-face
-      delimiter: _ :? @default))
-
-   :feature 'builtin
-   :language 'clojure
-   `(((list_lit :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)))
-
-   ;; Any function calls, not built-ins.
-   ;; This can give false positives (macros, quoted lists, namespace imports)
-   ;; but is a level 4 feature and never enabled by default.
-   :feature 'function
-   :language 'clojure
-   '((list_lit :anchor (sym_lit (sym_name) @font-lock-function-call-face)))
-
-   :feature 'symbol
-   :language 'clojure
-   '((sym_ns) @font-lock-type-face)
-
-   ;; How does this work for defns nested in other forms, not at the top level?
-   ;; Should I match against the source node to only hit the top level? Can 
that be expressed?
-   ;; What about valid usages like `(let [closed 1] (defn +closed [n] (+ n 
closed)))'??
-   ;; 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)
-                :anchor (sym_lit (sym_name) @font-lock-function-name-face))
-      (:match ,clojure-ts--definition-symbol-regexp @def))
-     ((anon_fn_lit
-       marker: "#" @font-lock-property-face)))
-
-   :feature 'variable ;; def, defonce
-   :language 'clojure
-   `(((list_lit :anchor (sym_lit (sym_name) @def)
-                :anchor (sym_lit (sym_name) @font-lock-variable-name-face))
-      (:match ,clojure-ts--variable-definition-symbol-regexp @def)))
-
-   ;; Can we support declarations in the namespace form?
-   :feature 'type
-   :language 'clojure
-   `(;; Type Declarations
-     ((list_lit :anchor (sym_lit (sym_name) @def)
-                :anchor (sym_lit (sym_name) @font-lock-type-face))
-      (:match ,clojure-ts--typedef-symbol-regexp @def))
-     ;; Type Hints
-     (meta_lit
-      marker: "^" @font-lock-operator-face
-      value: (sym_lit (sym_name) @font-lock-type-face))
-     (old_meta_lit
-      marker: "#^" @font-lock-operator-face
-      value: (sym_lit (sym_name) @font-lock-type-face)))
-
-   :feature 'metadata
-   :language 'clojure
-   :override t
-   `((meta_lit
-      marker: "^" @font-lock-operator-face
-      value: (kwd_lit (kwd_name) @font-lock-property-name-face))
-     (old_meta_lit
-      marker: "#^" @font-lock-operator-face
-      value: (kwd_lit (kwd_name) @font-lock-property-name-face)))
-
-   :feature 'tagged-literals
-   :language 'clojure
-   :override t
-   '((tagged_or_ctor_lit marker: "#" @font-lock-preprocessor-face
-                         tag: (sym_lit) @font-lock-preprocessor-face))
-
-   ;; Figure out how to highlight symbols in docstrings.
-   ;; Might require a markdown grammar
-   :feature 'doc
-   :language 'clojure
-   :override t
-   `(;; Captures docstrings in def, defonce
-     ((list_lit :anchor (sym_lit) @def_symbol
-                :anchor (sym_lit) ; variable name
-                :anchor (str_lit) @font-lock-doc-face
-                :anchor (_)) ; the variable's value
-      (:match ,clojure-ts--variable-definition-symbol-regexp @def_symbol))
-     ;; Captures docstrings defn, defmacro, ns, and things like that
-     ((list_lit :anchor (sym_lit) @def_symbol
-                :anchor (sym_lit) ; function_name
-                :anchor (str_lit) @font-lock-doc-face)
-      (:match ,clojure-ts--definition-symbol-regexp @def_symbol))
-     ;; Captures docstrings in defprotcol, definterface
-     ((list_lit :anchor (sym_lit) @def_symbol
-                (list_lit
-                 :anchor (sym_lit) (vec_lit) :*
-                 (str_lit) @font-lock-doc-face :anchor)
-                :*)
-      (:match ,clojure-ts--interface-def-symbol-regexp @def_symbol)))
-
-   :feature 'quote
-   :language 'clojure
-   '((quoting_lit
-      marker: _ @font-lock-delimiter-face)
-     (var_quoting_lit
-      marker: _ @font-lock-delimiter-face)
-     (syn_quoting_lit
-      marker: _ @font-lock-delimiter-face)
-     (unquoting_lit
-      marker: _ @font-lock-delimiter-face)
-     (unquote_splicing_lit
-      marker: _ @font-lock-delimiter-face)
-     (var_quoting_lit
-      marker: _ @font-lock-delimiter-face))
-
-   :feature 'bracket
-   :language 'clojure
-   '((["(" ")" "[" "]" "{" "}"]) @font-lock-bracket-face
-     (set_lit :anchor "#" @font-lock-bracket-face))
-
-   :feature 'comment
-   :language 'clojure
-   :override t
-   `((comment) @font-lock-comment-face
-     (dis_expr
-      marker: "#_" @font-lock-comment-delimiter-face
-      value: _ @font-lock-comment-face)
-     (,(append
-        '(list_lit :anchor (sym_lit) @font-lock-comment-delimiter-face)
-        (when clojure-ts-comment-macro-font-lock-body
-          '(_ :* @font-lock-comment-face)))
-      (:match "^\\(\\(clojure.core/\\)?comment\\)$" 
@font-lock-comment-delimiter-face)))
-
-   :feature 'deref ;; not part of clojure-mode, but a cool idea?
-   :language 'clojure
-   '((derefing_lit
-      marker: "@" @font-lock-warning-face))))
+(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
+               :anchor (sym_lit) ; variable name
+               :anchor (str_lit) ,capture-symbol
+               :anchor (_)) ; the variable's value
+     (:match ,clojure-ts--variable-definition-symbol-regexp @def_symbol))
+    ;; Captures docstrings defn, defmacro, ns, and things like that
+    ((list_lit :anchor (sym_lit) @def_symbol
+               :anchor (sym_lit) ; function_name
+               :anchor (str_lit) ,capture-symbol)
+     (:match ,clojure-ts--definition-symbol-regexp @def_symbol))
+    ;; Captures docstrings in defprotcol, definterface
+    ((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))))
+
+(defvar clojure-ts--treesit-range-settings
+  (treesit-range-rules
+   :embed 'markdown_inline
+   :host 'clojure
+   (clojure-ts--docstring-query '@capture)))
+
+(defun clojure-ts--font-lock-settings (markdown-available)
+  "Return font lock settings suitable for use in `treesit-font-lock-settings'.
+When MARKDOWN-AVAILABLE is non-nil, includes rules for highlighting docstrings
+with the markdown_inline grammar."
+  (append
+   (treesit-font-lock-rules
+    :feature 'string
+    :language 'clojure
+    '((str_lit) @font-lock-string-face
+      (regex_lit) @font-lock-regexp-face)
+
+    :feature 'regex
+    :language 'clojure
+    :override t
+    '((regex_lit marker: _ @font-lock-property-face))
+
+    :feature 'number
+    :language 'clojure
+    '((num_lit) @font-lock-number-face)
+
+    :feature 'constant
+    :language 'clojure
+    '([(bool_lit) (nil_lit)] @font-lock-constant-face)
+
+    :feature 'char
+    :language 'clojure
+    '((char_lit) @clojure-ts-character-face)
+
+    :feature 'keyword
+    :language 'clojure
+    '((kwd_ns) @font-lock-type-face
+      (kwd_name) @clojure-ts-keyword-face
+      (kwd_lit
+       marker: _ @clojure-ts-keyword-face
+       delimiter: _ :? @default))
+
+    :feature 'builtin
+    :language 'clojure
+    `(((list_lit :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)))
+
+    ;; Any function calls, not built-ins.
+    ;; This can give false positives (macros, quoted lists, namespace imports)
+    ;; but is a level 4 feature and never enabled by default.
+    :feature 'function
+    :language 'clojure
+    '((list_lit :anchor (sym_lit (sym_name) @font-lock-function-call-face)))
+
+    :feature 'symbol
+    :language 'clojure
+    '((sym_ns) @font-lock-type-face)
+
+    ;; How does this work for defns nested in other forms, not at the top 
level?
+    ;; Should I match against the source node to only hit the top level? Can 
that be expressed?
+    ;; What about valid usages like `(let [closed 1] (defn +closed [n] (+ n 
closed)))'??
+    ;; 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)
+                 :anchor (sym_lit (sym_name) @font-lock-function-name-face))
+       (:match ,clojure-ts--definition-symbol-regexp @def))
+      ((anon_fn_lit
+        marker: "#" @font-lock-property-face)))
+
+    :feature 'variable ;; def, defonce
+    :language 'clojure
+    `(((list_lit :anchor (sym_lit (sym_name) @def)
+                 :anchor (sym_lit (sym_name) @font-lock-variable-name-face))
+       (:match ,clojure-ts--variable-definition-symbol-regexp @def)))
+
+    ;; Can we support declarations in the namespace form?
+    :feature 'type
+    :language 'clojure
+    `(;; Type Declarations
+      ((list_lit :anchor (sym_lit (sym_name) @def)
+                 :anchor (sym_lit (sym_name) @font-lock-type-face))
+       (:match ,clojure-ts--typedef-symbol-regexp @def))
+      ;; Type Hints
+      (meta_lit
+       marker: "^" @font-lock-operator-face
+       value: (sym_lit (sym_name) @font-lock-type-face))
+      (old_meta_lit
+       marker: "#^" @font-lock-operator-face
+       value: (sym_lit (sym_name) @font-lock-type-face)))
+
+    :feature 'metadata
+    :language 'clojure
+    :override t
+    `((meta_lit
+       marker: "^" @font-lock-operator-face
+       value: (kwd_lit (kwd_name) @font-lock-property-name-face))
+      (old_meta_lit
+       marker: "#^" @font-lock-operator-face
+       value: (kwd_lit (kwd_name) @font-lock-property-name-face)))
+
+    :feature 'tagged-literals
+    :language 'clojure
+    :override t
+    '((tagged_or_ctor_lit marker: "#" @font-lock-preprocessor-face
+                          tag: (sym_lit) @font-lock-preprocessor-face))
+
+    :feature 'doc
+    :language 'clojure
+    :override t
+    (clojure-ts--docstring-query '@font-lock-doc-face))
+
+   (when markdown-available
+     (treesit-font-lock-rules
+      :feature 'doc
+      :language 'markdown_inline
+      :override t
+      `((inline
+          (code_span (code_span_delimiter) :* @font-lock-delimiter-face)
+          @font-lock-constant-face))))
+
+   (treesit-font-lock-rules
+    :feature 'quote
+    :language 'clojure
+    '((quoting_lit
+       marker: _ @font-lock-delimiter-face)
+      (var_quoting_lit
+       marker: _ @font-lock-delimiter-face)
+      (syn_quoting_lit
+       marker: _ @font-lock-delimiter-face)
+      (unquoting_lit
+       marker: _ @font-lock-delimiter-face)
+      (unquote_splicing_lit
+       marker: _ @font-lock-delimiter-face)
+      (var_quoting_lit
+       marker: _ @font-lock-delimiter-face))
+
+    :feature 'bracket
+    :language 'clojure
+    '((["(" ")" "[" "]" "{" "}"]) @font-lock-bracket-face
+      (set_lit :anchor "#" @font-lock-bracket-face))
+
+    :feature 'comment
+    :language 'clojure
+    :override t
+    `((comment) @font-lock-comment-face
+      (dis_expr
+       marker: "#_" @font-lock-comment-delimiter-face
+       value: _ @font-lock-comment-face)
+      (,(append
+         '(list_lit :anchor (sym_lit) @font-lock-comment-delimiter-face)
+         (when clojure-ts-comment-macro-font-lock-body
+           '(_ :* @font-lock-comment-face)))
+       (:match "^\\(\\(clojure.core/\\)?comment\\)$" 
@font-lock-comment-delimiter-face)))
+
+    :feature 'deref ;; not part of clojure-mode, but a cool idea?
+    :language 'clojure
+    '((derefing_lit
+       marker: "@" @font-lock-warning-face)))))
 
 ;; Node predicates
 
@@ -767,46 +794,74 @@ forms like deftype, defrecord, reify, proxy, etc."
     (set-keymap-parent map clojure-ts-mode-map)
     map))
 
-;;;###autolaod
-(add-to-list
- 'treesit-language-source-alist
- '(clojure "https://github.com/sogaiu/tree-sitter-clojure.git"; "v0.0.12"))
-
 (defun clojure-ts-mode-display-version ()
   "Display the current `clojure-mode-version' in the minibuffer."
   (interactive)
   (message "clojure-ts-mode (version %s)" clojure-ts-mode-version))
 
+(defconst clojure-ts-grammar-recipes
+  '((clojure "https://github.com/sogaiu/tree-sitter-clojure.git";
+             "v0.0.12")
+    (markdown_inline "https://github.com/MDeiml/tree-sitter-markdown";
+                     "v0.1.6"
+                     "tree-sitter-markdown-inline/src"))
+  "Intended to be used as the value for `treesit-language-source-alist'.")
+
+(defun clojure-ts--ensure-grammars ()
+  "Install required language grammars if not already available."
+  (when clojure-ts-ensure-grammars
+    (let ((treesit-language-source-alist clojure-ts-grammar-recipes))
+      (unless (treesit-language-available-p 'clojure nil)
+        (message "Installing clojure tree-sitter grammar.")
+        (treesit-install-language-grammar 'clojure))
+      (unless (treesit-language-available-p 'markdown_inline nil)
+        (message "Installing markdown tree-sitter grammar.")
+        (treesit-install-language-grammar 'markdown_inline)))))
+
+(defun clojure-ts-mode-variables (&optional markdown-available)
+  "Set up initial buffer-local variables for clojure-ts-mode.
+See `clojure-ts--font-lock-settings' for usage of MARKDOWN-AVAILABLE."
+  (setq-local comment-start ";")
+  (setq-local treesit-font-lock-settings
+              (clojure-ts--font-lock-settings markdown-available))
+  (setq-local treesit-defun-prefer-top-level t)
+  (setq-local treesit-defun-tactic 'top-level)
+  (setq-local treesit-defun-type-regexp
+              (rx (or "list_lit" "vec_lit" "map_lit")))
+  (setq-local treesit-simple-indent-rules
+              (clojure-ts--configured-indent-rules))
+  (setq-local treesit-defun-name-function
+              #'clojure-ts--standard-definition-node-name)
+  (setq-local treesit-simple-imenu-settings
+              clojure-ts--imenu-settings)
+  (setq-local treesit-font-lock-feature-list
+              '((comment definition variable)
+                (keyword string char symbol builtin type)
+                (constant number quote metadata doc)
+                (bracket deref function regex tagged-literals)))
+  (when (boundp 'treesit-thing-settings) ;; Emacs 30+
+    (setq-local treesit-thing-settings clojure-ts--thing-settings)))
+
 ;;;###autoload
 (define-derived-mode clojure-ts-mode prog-mode "Clojure[TS]"
   "Major mode for editing Clojure code.
 
 \\{clojure-ts-mode-map}"
   :syntax-table clojure-ts-mode-syntax-table
-  (unless (treesit-language-available-p 'clojure nil)
-    (treesit-install-language-grammar 'clojure))
-  (setq-local comment-start ";")
-  (when (treesit-ready-p 'clojure)
-    (treesit-parser-create 'clojure)
-    (setq-local treesit-font-lock-settings (clojure-ts--font-lock-settings)
-                treesit-defun-prefer-top-level t
-                treesit-defun-tactic 'top-level
-                treesit-defun-type-regexp (rx (or "list_lit" "vec_lit" 
"map_lit"))
-                treesit-simple-indent-rules 
(clojure-ts--configured-indent-rules)
-                treesit-defun-name-function 
#'clojure-ts--standard-definition-node-name
-                treesit-simple-imenu-settings clojure-ts--imenu-settings
-                treesit-font-lock-feature-list
-                '((comment definition variable)
-                  (keyword string char symbol builtin type)
-                  (constant number quote metadata doc)
-                  (bracket deref function regex tagged-literals)))
-    (when (boundp 'treesit-thing-settings) ;; Emacs 30+
-      (setq-local treesit-thing-settings clojure-ts--thing-settings))
-    (when clojure-ts--debug
-      (setq-local treesit--indent-verbose t
-                  treesit--font-lock-verbose t)
-      (treesit-inspect-mode))
-    (treesit-major-mode-setup)))
+  (clojure-ts--ensure-grammars)
+  (let ((markdown-available (treesit-ready-p 'markdown_inline t)))
+    (when markdown-available
+      (treesit-parser-create 'markdown_inline)
+      (setq-local treesit-range-settings clojure-ts--treesit-range-settings))
+    (when (treesit-ready-p 'clojure)
+      (treesit-parser-create 'clojure)
+      (clojure-ts-mode-variables markdown-available)
+      (when clojure-ts--debug
+        (setq-local treesit--indent-verbose t)
+        (when (eq clojure-ts--debug 'font-lock)
+          (setq-local treesit--font-lock-verbose t))
+        (treesit-inspect-mode))
+      (treesit-major-mode-setup))))
 
 ;;;###autoload
 (define-derived-mode clojurescript-ts-mode clojure-ts-mode "ClojureScript[TS]"
diff --git a/test/indentation.clj b/test/indentation.clj
index 520adc9987..a5fe041f0c 100644
--- a/test/indentation.clj
+++ b/test/indentation.clj
@@ -1,4 +1,5 @@
 (ns indentation
+  "Docstring `important`. asdf"
   (:require
    [clojure.string :as str])
   (:import
@@ -79,7 +80,8 @@
 
 
 (defprotocol IProto
-  (foo [this x])
+  (foo [this x]
+    "`this` is a docstring.")
   (bar [this y]))
 
 (deftype MyThing []



reply via email to

[Prev in Thread] Current Thread [Next in Thread]