@@ -128,6 +128,18 @@ double quotes on the third column."
128128 :safe #'booleanp
129129 :package-version '(clojure-ts-mode . " 0.4" ))
130130
131+ (defcustom clojure-ts-clojurescript-use-js-parser t
132+ " When non-nil, use JS grammar to highlight syntax in js* forms."
133+ :type 'boolean
134+ :safe #'booleanp
135+ :package-version '(clojure-ts-mode . " 0.5" ))
136+
137+ (defcustom clojure-ts-jank-use-cpp-parser t
138+ " When non-nil, use C++ grammar to highlight syntax in native/raw forms."
139+ :type 'boolean
140+ :safe #'booleanp
141+ :package-version '(clojure-ts-mode . " 0.5" ))
142+
131143(defcustom clojure-ts-auto-remap t
132144 " When non-nil, redirect all `clojure-mode' buffers to `clojure-ts-mode' ."
133145 :safe #'booleanp
@@ -489,6 +501,34 @@ When USE-REGEX is non-nil, include range settings for regex parser."
489501 :local t
490502 '((regex_content) @capture)))))
491503
504+ (defun clojure-ts--fontify-string (node override _start _end &optional _rest )
505+ " Fontify string content NODE with `font-lock-string-face' .
506+
507+ In order to support embedded syntax highlighting for JS in ClojureScript
508+ and C++ in Jank we need to avoid fontifying string content in some
509+ special forms, such as native/raw in Jank and js* in ClojureScript,
510+ otherwise string face will interfere with embedded parser's faces.
511+
512+ This function respects OVERRIDE argument by passing it to
513+ `treesit-fontify-with-override' .
514+
515+ START and END arguments that are passed to this function are not start
516+ and end of the NODE, so we ignore them."
517+ (let* ((prev (treesit-node-prev-sibling (treesit-node-parent node)))
518+ (jank-native-p (and (derived-mode-p 'clojure-ts-jank-mode )
519+ clojure-ts-jank-use-cpp-parser
520+ (clojure-ts--symbol-node-p prev)
521+ (string= (treesit-node-text prev) " native/raw" )))
522+ (js-interop-p (and (derived-mode-p 'clojure-ts-clojurescript-mode )
523+ clojure-ts-clojurescript-use-js-parser
524+ (clojure-ts--symbol-node-p prev)
525+ (string= (treesit-node-text prev) " js*" ))))
526+ (when (not (or jank-native-p js-interop-p))
527+ (treesit-fontify-with-override (treesit-node-start node)
528+ (treesit-node-end node)
529+ 'font-lock-string-face
530+ override))))
531+
492532(defun clojure-ts--font-lock-settings (markdown-available regex-available )
493533 " Return font lock settings suitable for use in `treesit-font-lock-settings' .
494534
@@ -501,7 +541,9 @@ literals with regex grammar."
501541 (treesit-font-lock-rules
502542 :feature 'string
503543 :language 'clojure
504- '((str_lit) @font-lock-string-face
544+ '((str_lit open: _ @font-lock-string-face
545+ (str_content) @clojure-ts--fontify-string
546+ close: _ @font-lock-string-face)
505547 (regex_lit) @font-lock-regexp-face)
506548
507549 :feature 'regex
@@ -1400,7 +1442,6 @@ regexes with anchors matching the beginning and end of the line are
14001442used."
14011443 `((clojure
14021444 ((parent-is " ^source$" ) parent-bol 0 )
1403- (clojure-ts--match-docstring parent 0 )
14041445 ; ; Literal Sequences
14051446 ((parent-is " ^vec_lit$" ) parent 1 ) ; ; https://guide.clojure.style/#bindings-alignment
14061447 ((parent-is " ^map_lit$" ) parent 1 ) ; ; https://guide.clojure.style/#map-keys-alignment
@@ -1418,7 +1459,12 @@ used."
14181459 ; ; https://guide.clojure.style/#one-space-indent
14191460 ((parent-is " ^list_lit$" ) parent 1 )
14201461 ((parent-is " ^anon_fn_lit$" ) parent 2 )
1421- (clojure-ts--match-with-metadata parent 0 ))))
1462+ (clojure-ts--match-with-metadata parent 0 )
1463+ ; ; This is slow and only matches when point is inside of a docstring and
1464+ ; ; only when Markdown grammar is disabled. `indent-region' tries to match
1465+ ; ; all the rules from top to bottom, so order matters here (the slowest
1466+ ; ; rules should be at the bottom).
1467+ (clojure-ts--match-docstring parent 0 ))))
14221468
14231469(defun clojure-ts--configured-indent-rules ()
14241470 " Gets the configured choice of indent rules."
@@ -2518,6 +2564,44 @@ function can also be used to upgrade the grammars if they are outdated."
25182564 (let ((treesit-language-source-alist clojure-ts-grammar-recipes))
25192565 (treesit-install-language-grammar grammar)))))
25202566
2567+ (defsubst clojure-ts--font-lock-setting-update-override (setting )
2568+ " Return SETTING with override set to TRUE."
2569+ (let ((new-setting (copy-tree setting)))
2570+ (setf (nth 3 new-setting) t )
2571+ new-setting))
2572+
2573+ (defun clojure-ts--harvest-treesit-configs (mode )
2574+ " Harvest tree-sitter configs from MODE.
2575+ Return a plist with the following keys and value:
2576+
2577+ :font-lock (from `treesit-font-lock-settings' )
2578+ :simple-indent (from `treesit-simple-indent-rules' )"
2579+ (with-temp-buffer
2580+ (funcall mode)
2581+ ; ; We need to set :override t for all external queries, otherwise new faces
2582+ ; ; won't be applied on top of the string face defined for `clojure-ts-mode' .
2583+ (list :font-lock (seq-map #'clojure-ts--font-lock-setting-update-override
2584+ treesit-font-lock-settings)
2585+ :simple-indent treesit-simple-indent-rules)))
2586+
2587+ (defun clojure-ts--add-config-for-mode (mode )
2588+ " Add configurations for MODE to current buffer.
2589+
2590+ Configuration includes font-lock and indent. For font-lock rules, use
2591+ the same features enabled in MODE."
2592+ (let ((configs (clojure-ts--harvest-treesit-configs mode)))
2593+ (setq treesit-font-lock-settings
2594+ (append treesit-font-lock-settings
2595+ (plist-get configs :font-lock )))
2596+ ; ; FIXME: This works a bit aggressively. `indent-region' always tries to
2597+ ; ; use rules for embedded parser. Without it users can format embedded code
2598+ ; ; in an arbitrary way.
2599+ ; ;
2600+ ; ; (setq treesit-simple-indent-rules
2601+ ; ; (append treesit-simple-indent-rules
2602+ ; ; (plist-get configs :simple-indent)))
2603+ ))
2604+
25212605(defun clojure-ts-mode-variables (&optional markdown-available regex-available )
25222606 " Initialize buffer-local variables for `clojure-ts-mode' .
25232607
@@ -2625,7 +2709,20 @@ REGEX-AVAILABLE."
26252709(define-derived-mode clojure-ts-clojurescript-mode clojure-ts-mode " ClojureScript[TS]"
26262710 " Major mode for editing ClojureScript code.
26272711
2628- \\ {clojure-ts-clojurescript-mode-map}" )
2712+ \\ {clojure-ts-clojurescript-mode-map}"
2713+ (when (and clojure-ts-clojurescript-use-js-parser
2714+ (treesit-ready-p 'javascript t ))
2715+ (setq-local treesit-range-settings
2716+ (append treesit-range-settings
2717+ (treesit-range-rules
2718+ :embed 'javascript
2719+ :host 'clojure
2720+ :local t
2721+ '(((list_lit (sym_lit) @_sym-name
2722+ :anchor (str_lit (str_content) @capture))
2723+ (:equal @_sym-name " js*" ))))))
2724+ (clojure-ts--add-config-for-mode 'js-ts-mode )
2725+ (treesit-major-mode-setup)))
26292726
26302727;;;### autoload
26312728(define-derived-mode clojure-ts-clojurec-mode clojure-ts-mode " ClojureC[TS]"
@@ -2643,7 +2740,20 @@ REGEX-AVAILABLE."
26432740(define-derived-mode clojure-ts-jank-mode clojure-ts-mode " Jank[TS]"
26442741 " Major mode for editing Jank code.
26452742
2646- \\ {clojure-ts-jank-mode-map}" )
2743+ \\ {clojure-ts-jank-mode-map}"
2744+ (when (and clojure-ts-jank-use-cpp-parser
2745+ (treesit-ready-p 'cpp t ))
2746+ (setq-local treesit-range-settings
2747+ (append treesit-range-settings
2748+ (treesit-range-rules
2749+ :embed 'cpp
2750+ :host 'clojure
2751+ :local t
2752+ '(((list_lit (sym_lit) @_sym-name
2753+ :anchor (str_lit (str_content) @capture))
2754+ (:equal @_sym-name " native/raw" ))))))
2755+ (clojure-ts--add-config-for-mode 'c++-ts-mode )
2756+ (treesit-major-mode-setup)))
26472757
26482758(defun clojure-ts--register-novel-modes ()
26492759 " Set up Clojure modes not present in progenitor clojure-mode.el."
0 commit comments