Skip to content

Commit 08669ec

Browse files
committed
[#11] Add regex syntax highlighting
1 parent 61041c8 commit 08669ec

File tree

4 files changed

+115
-19
lines changed

4 files changed

+115
-19
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
## main (unreleased)
44

55
- [#16](https://github.com/clojure-emacs/clojure-ts-mode/issues/16): Introduce `clojure-ts-align`.
6+
- [#11](https://github.com/clojure-emacs/clojure-ts-mode/issues/11): Enable regex syntax highlighting.
67

78
## 0.3.0 (2025-04-15)
89

README.md

+18-1
Original file line numberDiff line numberDiff line change
@@ -293,12 +293,29 @@ highlighted like regular clojure code.
293293
### Highlight markdown syntax in docstrings
294294

295295
By default markdown syntax is highlighted in the docstrings using
296-
`markdown_inline` grammar. To disable this feature set
296+
`markdown-inline` grammar. To disable this feature set
297297

298298
``` emacs-lisp
299299
(setopt clojure-ts-use-markdown-inline nil)
300300
```
301301

302+
Example of syntax highlighting:
303+
304+
<img width="512" src="/screenshots/markdown-syntax-dark-theme.png">
305+
306+
### Highlight regex syntax
307+
308+
By default syntax inside regex literals is highlighted using [regex](https://github.com/tree-sitter/tree-sitter-regex) grammar. To
309+
disable this feature set
310+
311+
```emacs-lisp
312+
(setopt clojure-ts-use-regex-parser nil)
313+
```
314+
315+
Example of syntax highlighting:
316+
317+
<img width="512" src="/screenshots/regex-syntax-dark-theme.png">
318+
302319
### Navigation and Evaluation
303320

304321
To make forms inside of `(comment ...)` forms appear as top-level forms for evaluation and navigation, set

clojure-ts-mode.el

+89-18
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,12 @@ double quotes on the third column."
121121
:safe #'booleanp
122122
:package-version '(clojure-ts-mode . "0.2.3"))
123123

124+
(defcustom clojure-ts-use-regex-parser t
125+
"When non-nil, use separate grammar to highlight regex syntax."
126+
:type 'boolean
127+
:safe #'booleanp
128+
:package-version '(clojure-ts-mode . "0.4"))
129+
124130
(defcustom clojure-ts-auto-remap t
125131
"When non-nil, redirect all `clojure-mode' buffers to `clojure-ts-mode'."
126132
:safe #'booleanp
@@ -407,17 +413,37 @@ if a third argument (the value) is provided.
407413
:*)
408414
(:match ,clojure-ts--interface-def-symbol-regexp @_def_symbol))))
409415

410-
(defvar clojure-ts--treesit-range-settings
411-
(treesit-range-rules
412-
:embed 'markdown-inline
413-
:host 'clojure
414-
:local t
415-
(clojure-ts--docstring-query '@capture)))
416+
(defun clojure-ts--treesit-range-settings (use-markdown-inline use-regex)
417+
"Return value for `treesit-range-settings' for `clojure-ts-mode'.
416418
417-
(defun clojure-ts--font-lock-settings (markdown-available)
419+
When USE-MARKDOWN-INLINE is non-nil, include range settings for
420+
markdown-inline parser.
421+
422+
When USE-REGEX is non-nil, include range settings for regex parser."
423+
(append
424+
(when use-markdown-inline
425+
(treesit-range-rules
426+
:embed 'markdown-inline
427+
:host 'clojure
428+
:offset '(1 . -1)
429+
:local t
430+
(clojure-ts--docstring-query '@capture)))
431+
(when use-regex
432+
(treesit-range-rules
433+
:embed 'regex
434+
:host 'clojure
435+
:offset '(2 . -1)
436+
:local t
437+
'((regex_lit) @capture)))))
438+
439+
(defun clojure-ts--font-lock-settings (markdown-available regex-available)
418440
"Return font lock settings suitable for use in `treesit-font-lock-settings'.
441+
419442
When MARKDOWN-AVAILABLE is non-nil, includes rules for highlighting docstrings
420-
with the markdown-inline grammar."
443+
with the markdown-inline grammar.
444+
445+
When REGEX-AVAILABLE is non-nil, includes rules for highlighting regex
446+
literals with regex grammar."
421447
(append
422448
(treesit-font-lock-rules
423449
:feature 'string
@@ -590,6 +616,44 @@ with the markdown-inline grammar."
590616
(inline_link (link_destination) @font-lock-constant-face)
591617
(shortcut_link (link_text) @link)])))
592618

619+
(when regex-available
620+
;; Queries are adapted from
621+
;; https://github.com/tree-sitter/tree-sitter-regex/blob/v0.24.3/queries/highlights.scm.
622+
(treesit-font-lock-rules
623+
:feature 'regex
624+
:language 'regex
625+
:override t
626+
'((["("
627+
")"
628+
"(?"
629+
"(?:"
630+
"(?<"
631+
"(?P<"
632+
"(?P="
633+
">"
634+
"["
635+
"]"
636+
"{"
637+
"}"
638+
"[:"
639+
":]"] @font-lock-regexp-grouping-construct)
640+
(["*"
641+
"+"
642+
"?"
643+
"|"
644+
"="
645+
"!"] @font-lock-property-name-face)
646+
((group_name) @font-lock-variable-name-face)
647+
((count_quantifier
648+
[(decimal_digits) @font-lock-number-face
649+
"," @font-lock-delimiter-face]))
650+
((flags) @font-lock-constant-face)
651+
((character_class
652+
["^" @font-lock-escape-face
653+
(class_range "-" @font-lock-escape-face)]))
654+
((identity_escape) @font-lock-builtin-face)
655+
([(start_assertion) (end_assertion)] @font-lock-constant-face))))
656+
593657
(treesit-font-lock-rules
594658
:feature 'quote
595659
:language 'clojure
@@ -1555,7 +1619,9 @@ between BEG and END."
15551619
"v0.0.13")
15561620
(markdown-inline "https://github.com/MDeiml/tree-sitter-markdown"
15571621
"v0.4.1"
1558-
"tree-sitter-markdown-inline/src"))
1622+
"tree-sitter-markdown-inline/src")
1623+
(regex "https://github.com/tree-sitter/tree-sitter-regex"
1624+
"v0.24.3"))
15591625
"Intended to be used as the value for `treesit-language-source-alist'.")
15601626

15611627
(defun clojure-ts--ensure-grammars ()
@@ -1584,20 +1650,22 @@ function can also be used to upgrade the grammars if they are outdated."
15841650
(let ((treesit-language-source-alist clojure-ts-grammar-recipes))
15851651
(treesit-install-language-grammar grammar)))))
15861652

1587-
(defun clojure-ts-mode-variables (&optional markdown-available)
1653+
(defun clojure-ts-mode-variables (&optional markdown-available regex-available)
15881654
"Initialize buffer-local variables for `clojure-ts-mode'.
1589-
See `clojure-ts--font-lock-settings' for usage of MARKDOWN-AVAILABLE."
1655+
1656+
See `clojure-ts--font-lock-settings' for usage of MARKDOWN-AVAILABLE and
1657+
REGEX-AVAILABLE."
15901658
(setq-local indent-tabs-mode nil)
15911659
(setq-local comment-add 1)
15921660
(setq-local comment-start ";")
15931661

15941662
(setq-local treesit-font-lock-settings
1595-
(clojure-ts--font-lock-settings markdown-available))
1663+
(clojure-ts--font-lock-settings markdown-available regex-available))
15961664
(setq-local treesit-font-lock-feature-list
15971665
'((comment definition variable)
15981666
(keyword string char symbol builtin type)
1599-
(constant number quote metadata doc)
1600-
(bracket deref function regex tagged-literals)))
1667+
(constant number quote metadata doc regex)
1668+
(bracket deref function tagged-literals)))
16011669

16021670
(setq-local treesit-defun-prefer-top-level t)
16031671
(setq-local treesit-defun-tactic 'top-level)
@@ -1630,13 +1698,16 @@ See `clojure-ts--font-lock-settings' for usage of MARKDOWN-AVAILABLE."
16301698
:syntax-table clojure-ts-mode-syntax-table
16311699
(clojure-ts--ensure-grammars)
16321700
(let ((use-markdown-inline (and clojure-ts-use-markdown-inline
1633-
(treesit-ready-p 'markdown-inline t))))
1634-
(when use-markdown-inline
1635-
(setq-local treesit-range-settings clojure-ts--treesit-range-settings))
1701+
(treesit-ready-p 'markdown-inline t)))
1702+
(use-regex (and clojure-ts-use-regex-parser
1703+
(treesit-ready-p 'regex t))))
1704+
(setq-local treesit-range-settings
1705+
(clojure-ts--treesit-range-settings use-markdown-inline
1706+
use-regex))
16361707

16371708
(when (treesit-ready-p 'clojure)
16381709
(treesit-parser-create 'clojure)
1639-
(clojure-ts-mode-variables use-markdown-inline)
1710+
(clojure-ts-mode-variables use-markdown-inline use-regex)
16401711

16411712
(when clojure-ts--debug
16421713
(setq-local treesit--indent-verbose t)

test/samples/regex.clj

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
(ns regex)
2+
3+
(def email-pattern
4+
#"[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?")
5+
6+
(def simple-regex
7+
#"^(\\d+).*[a|b|c|d].*[a-z0-9!#]$")

0 commit comments

Comments
 (0)