branch: elpa/clojure-ts-mode commit 605adba8babdc82013cd9a9a9f15a8f3ae587e64 Author: Roman Rudakov <rruda...@fastmail.com> Commit: Bozhidar Batsov <bozhi...@batsov.dev>
[#11] Add regex syntax highlighting --- CHANGELOG.md | 1 + README.md | 19 ++++- clojure-ts-mode.el | 107 ++++++++++++++++++++++++----- screenshots/markdown-syntax-dark-theme.png | Bin 0 -> 59689 bytes screenshots/regex-syntax-dark-theme.png | Bin 0 -> 50440 bytes test/samples/regex.clj | 7 ++ 6 files changed, 115 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 41e2a140b1..8c11acd343 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## main (unreleased) - [#16](https://github.com/clojure-emacs/clojure-ts-mode/issues/16): Introduce `clojure-ts-align`. +- [#11](https://github.com/clojure-emacs/clojure-ts-mode/issues/11): Enable regex syntax highlighting. ## 0.3.0 (2025-04-15) diff --git a/README.md b/README.md index f44f5839c1..0c96c55c8e 100644 --- a/README.md +++ b/README.md @@ -293,12 +293,29 @@ highlighted like regular clojure code. ### Highlight markdown syntax in docstrings By default markdown syntax is highlighted in the docstrings using -`markdown_inline` grammar. To disable this feature set +`markdown-inline` grammar. To disable this feature set ``` emacs-lisp (setopt clojure-ts-use-markdown-inline nil) ``` +Example of syntax highlighting: + +<img width="512" src="/screenshots/markdown-syntax-dark-theme.png"> + +### Highlight regex syntax + +By default syntax inside regex literals is highlighted using [regex](https://github.com/tree-sitter/tree-sitter-regex) grammar. To +disable this feature set + +```emacs-lisp +(setopt clojure-ts-use-regex-parser nil) +``` + +Example of syntax highlighting: + +<img width="512" src="/screenshots/regex-syntax-dark-theme.png"> + ### Navigation and Evaluation To make forms inside of `(comment ...)` forms appear as top-level forms for evaluation and navigation, set diff --git a/clojure-ts-mode.el b/clojure-ts-mode.el index 7c16c01785..f88f342158 100644 --- a/clojure-ts-mode.el +++ b/clojure-ts-mode.el @@ -121,6 +121,12 @@ double quotes on the third column." :safe #'booleanp :package-version '(clojure-ts-mode . "0.2.3")) +(defcustom clojure-ts-use-regex-parser t + "When non-nil, use separate grammar to highlight regex syntax." + :type 'boolean + :safe #'booleanp + :package-version '(clojure-ts-mode . "0.4")) + (defcustom clojure-ts-auto-remap t "When non-nil, redirect all `clojure-mode' buffers to `clojure-ts-mode'." :safe #'booleanp @@ -407,17 +413,37 @@ if a third argument (the value) is provided. :*) (:match ,clojure-ts--interface-def-symbol-regexp @_def_symbol)))) -(defvar clojure-ts--treesit-range-settings - (treesit-range-rules - :embed 'markdown-inline - :host 'clojure - :local t - (clojure-ts--docstring-query '@capture))) +(defun clojure-ts--treesit-range-settings (use-markdown-inline use-regex) + "Return value for `treesit-range-settings' for `clojure-ts-mode'. -(defun clojure-ts--font-lock-settings (markdown-available) +When USE-MARKDOWN-INLINE is non-nil, include range settings for +markdown-inline parser. + +When USE-REGEX is non-nil, include range settings for regex parser." + (append + (when use-markdown-inline + (treesit-range-rules + :embed 'markdown-inline + :host 'clojure + :offset '(1 . -1) + :local t + (clojure-ts--docstring-query '@capture))) + (when use-regex + (treesit-range-rules + :embed 'regex + :host 'clojure + :offset '(2 . -1) + :local t + '((regex_lit) @capture))))) + +(defun clojure-ts--font-lock-settings (markdown-available regex-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." +with the markdown-inline grammar. + +When REGEX-AVAILABLE is non-nil, includes rules for highlighting regex +literals with regex grammar." (append (treesit-font-lock-rules :feature 'string @@ -590,6 +616,44 @@ with the markdown-inline grammar." (inline_link (link_destination) @font-lock-constant-face) (shortcut_link (link_text) @link)]))) + (when regex-available + ;; Queries are adapted from + ;; https://github.com/tree-sitter/tree-sitter-regex/blob/v0.24.3/queries/highlights.scm. + (treesit-font-lock-rules + :feature 'regex + :language 'regex + :override t + '((["(" + ")" + "(?" + "(?:" + "(?<" + "(?P<" + "(?P=" + ">" + "[" + "]" + "{" + "}" + "[:" + ":]"] @font-lock-regexp-grouping-construct) + (["*" + "+" + "?" + "|" + "=" + "!"] @font-lock-property-name-face) + ((group_name) @font-lock-variable-name-face) + ((count_quantifier + [(decimal_digits) @font-lock-number-face + "," @font-lock-delimiter-face])) + ((flags) @font-lock-constant-face) + ((character_class + ["^" @font-lock-escape-face + (class_range "-" @font-lock-escape-face)])) + ((identity_escape) @font-lock-builtin-face) + ([(start_assertion) (end_assertion)] @font-lock-constant-face)))) + (treesit-font-lock-rules :feature 'quote :language 'clojure @@ -1555,7 +1619,9 @@ between BEG and END." "v0.0.13") (markdown-inline "https://github.com/MDeiml/tree-sitter-markdown" "v0.4.1" - "tree-sitter-markdown-inline/src")) + "tree-sitter-markdown-inline/src") + (regex "https://github.com/tree-sitter/tree-sitter-regex" + "v0.24.3")) "Intended to be used as the value for `treesit-language-source-alist'.") (defun clojure-ts--ensure-grammars () @@ -1584,20 +1650,22 @@ function can also be used to upgrade the grammars if they are outdated." (let ((treesit-language-source-alist clojure-ts-grammar-recipes)) (treesit-install-language-grammar grammar))))) -(defun clojure-ts-mode-variables (&optional markdown-available) +(defun clojure-ts-mode-variables (&optional markdown-available regex-available) "Initialize buffer-local variables for `clojure-ts-mode'. -See `clojure-ts--font-lock-settings' for usage of MARKDOWN-AVAILABLE." + +See `clojure-ts--font-lock-settings' for usage of MARKDOWN-AVAILABLE and +REGEX-AVAILABLE." (setq-local indent-tabs-mode nil) (setq-local comment-add 1) (setq-local comment-start ";") (setq-local treesit-font-lock-settings - (clojure-ts--font-lock-settings markdown-available)) + (clojure-ts--font-lock-settings markdown-available regex-available)) (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))) + (constant number quote metadata doc regex) + (bracket deref function tagged-literals))) (setq-local treesit-defun-prefer-top-level t) (setq-local treesit-defun-tactic 'top-level) @@ -1630,13 +1698,16 @@ See `clojure-ts--font-lock-settings' for usage of MARKDOWN-AVAILABLE." :syntax-table clojure-ts-mode-syntax-table (clojure-ts--ensure-grammars) (let ((use-markdown-inline (and clojure-ts-use-markdown-inline - (treesit-ready-p 'markdown-inline t)))) - (when use-markdown-inline - (setq-local treesit-range-settings clojure-ts--treesit-range-settings)) + (treesit-ready-p 'markdown-inline t))) + (use-regex (and clojure-ts-use-regex-parser + (treesit-ready-p 'regex t)))) + (setq-local treesit-range-settings + (clojure-ts--treesit-range-settings use-markdown-inline + use-regex)) (when (treesit-ready-p 'clojure) (treesit-parser-create 'clojure) - (clojure-ts-mode-variables use-markdown-inline) + (clojure-ts-mode-variables use-markdown-inline use-regex) (when clojure-ts--debug (setq-local treesit--indent-verbose t) diff --git a/screenshots/markdown-syntax-dark-theme.png b/screenshots/markdown-syntax-dark-theme.png new file mode 100644 index 0000000000..7a908acb90 Binary files /dev/null and b/screenshots/markdown-syntax-dark-theme.png differ diff --git a/screenshots/regex-syntax-dark-theme.png b/screenshots/regex-syntax-dark-theme.png new file mode 100644 index 0000000000..ad7ee45dd7 Binary files /dev/null and b/screenshots/regex-syntax-dark-theme.png differ diff --git a/test/samples/regex.clj b/test/samples/regex.clj new file mode 100644 index 0000000000..f37b50a35e --- /dev/null +++ b/test/samples/regex.clj @@ -0,0 +1,7 @@ +(ns regex) + +(def email-pattern + #"[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?") + +(def simple-regex + #"^(\\d+).*[a|b|c|d].*[a-z0-9!#]$")