From: Juri Linkov Date: Tue, 1 Apr 2025 17:17:17 +0000 (+0300) Subject: New treesit generic mode 'liquid-generic-ts-mode' (bug#77255) X-Git-Url: http://git.eshelyaron.com/gitweb/?a=commitdiff_plain;h=d010a0cf23a8758f185bb42406c3db8d461068b2;p=emacs.git New treesit generic mode 'liquid-generic-ts-mode' (bug#77255) * lisp/treesit.el (treesit-replace-font-lock-feature-settings): Check the query language in addition to checking the feature. (treesit-font-lock-fontify-region): Use treesit-font-lock-setting-* accessors. * lisp/treesit-x.el (treesit-generic-mode-setup): Append new font-lock rules to an existing treesit-font-lock-settings possibly inherited from the parent. (treesit-generic-mode-setup): Use treesit-merge-font-lock-feature-list to merge with an existing feature list inherited from the parent. (liquid-generic-ts-mode): New treesit generic mode. (alpinejs-generic-ts-setup): New treesit generic setup. (cherry picked from commit 87da719b6c4a53a31c67f3a9646b68cb15a1ffe7) --- diff --git a/lisp/treesit-x.el b/lisp/treesit-x.el index 881e11e4e7a..5989bb89850 100644 --- a/lisp/treesit-x.el +++ b/lisp/treesit-x.el @@ -147,11 +147,15 @@ of `define-treesit-generic-mode'. (when-let* ((query (treesit-generic-mode-font-lock-query lang))) (setq-local treesit-font-lock-settings - (treesit-font-lock-rules - :language lang - :feature 'highlights - query)) - (setq-local treesit-font-lock-feature-list '((highlights)))))) + (append treesit-font-lock-settings + (treesit-font-lock-rules + :language lang + :feature 'highlights + query))) + (setq-local treesit-font-lock-feature-list + (treesit-merge-font-lock-feature-list + treesit-font-lock-feature-list + '((highlights))))))) ;;; Generic font-lock handling @@ -202,6 +206,112 @@ of `define-treesit-generic-mode'. (setq-local comment-start "# ") (setq-local comment-end "")) +(define-treesit-generic-mode liquid-generic-ts-mode + "Tree-sitter generic mode for Liquid templates." + :lang 'liquid + :source "https://github.com/hankthetank27/tree-sitter-liquid" + :auto-mode "\\.liquid\\'" + :name "Liquid" + :parent mhtml-ts-mode + + (setq-local treesit-range-settings + (append treesit-range-settings + (treesit-range-rules + :embed 'html + :host 'liquid + '(((template_content) @cap))))) + + (setq-local treesit-thing-settings + (append treesit-thing-settings + `((liquid (sexp (not ,(rx bos "program" eos))) + (list ,(rx bos (or "range" + "if_statement" + "for_loop_statement" + "case_statement" + "unless_statement" + "capture_statement" + "form_statement" + "tablerow_statement" + "paginate_statement") + eos)))))) + + (setq-local treesit-aggregated-outline-predicate + (append treesit-aggregated-outline-predicate + `((liquid . ,(rx bos (or "if_statement" + "for_loop_statement") + eos))))) + + (when (treesit-ready-p 'yaml t) + (defvar yaml-ts-mode--font-lock-settings) + (require 'yaml-ts-mode) + (setq-local treesit-range-settings + (append treesit-range-settings + (treesit-range-rules + :embed 'yaml + :host 'liquid + :local t + '(((front_matter) @cap))))) + (setq-local treesit-font-lock-settings + (append treesit-font-lock-settings + yaml-ts-mode--font-lock-settings)))) + +(defvar alpinejs-generic-ts-attr-regexp + (rx bos (or "x-" ":" "@")) + "Regexp for HTML attributes handled by Alpine.js") + +(defun alpinejs-generic-ts-attr-not-match (node) + (not (string-match-p alpinejs-generic-ts-attr-regexp + (treesit-node-text node t)))) + +(defun alpinejs-generic-ts-setup () + "Tree-sitter generic setup for Alpine.js framework. +It uses JavaScript highlighting inside a limited set of HTML attributes. +Intended to be used in combination with such major modes as +`mhtml-ts-mode' and `liquid-generic-ts-mode'. For example: + +\(add-hook \\='mhtml-ts-mode-hook \\='alpinejs-generic-ts-setup) +\(add-hook \\='liquid-generic-ts-mode-hook \\='alpinejs-generic-ts-setup)" + + ;; Use JavaScript highlighting for Alpinejs HTML attributes + (setq-local treesit-range-settings + (append treesit-range-settings + (treesit-range-rules + :embed 'javascript + :host 'html + :local t + `((attribute + (attribute_name) @_name + (:match ,alpinejs-generic-ts-attr-regexp @_name) + (quoted_attribute_value + (attribute_value) @cap)))))) + + ;; Highlight only non-Alpinejs HTML attributes + (setq-local treesit-font-lock-settings + (treesit-replace-font-lock-feature-settings + (treesit-font-lock-rules + :language 'html + :override t + :feature 'string + `((attribute + (attribute_name) @_name + (:pred alpinejs-generic-ts-attr-not-match @_name) + (quoted_attribute_value) @font-lock-string-face))) + treesit-font-lock-settings)) + + ;; Highlight only quotes for Alpinejs HTML attributes + (setq-local treesit-font-lock-settings + (append treesit-font-lock-settings + (treesit-font-lock-rules + :language 'html + :override t + :feature 'string + `((attribute + (attribute_name) @_name + (:match ,alpinejs-generic-ts-attr-regexp @_name) + (quoted_attribute_value "\"" @font-lock-string-face)))))) + + (treesit-major-mode-setup)) + (provide 'treesit-x) ;;; treesit-x.el ends here diff --git a/lisp/treesit.el b/lisp/treesit.el index cf8db3d3bc0..6db95bd07db 100644 --- a/lisp/treesit.el +++ b/lisp/treesit.el @@ -1632,12 +1632,16 @@ Both SETTINGS and NEW-SETTINGS must be a value suitable for Return a value suitable for `treesit-font-lock-settings'" (let ((result nil)) (dolist (new-setting new-settings) - (let ((new-feature (treesit-font-lock-setting-feature new-setting))) - (dolist (setting settings) - (let ((feature (treesit-font-lock-setting-feature setting))) - (if (eq new-feature feature) - (push new-setting result) - (push setting result)))))) + (let ((new-feature (treesit-font-lock-setting-feature new-setting)) + (new-lang (treesit-query-language + (treesit-font-lock-setting-query new-setting)))) + (dolist (setting settings) + (let ((feature (treesit-font-lock-setting-feature setting)) + (lang (treesit-query-language + (treesit-font-lock-setting-query setting)))) + (if (and (eq new-lang lang) (eq new-feature feature)) + (push new-setting result) + (push setting result)))))) (nreverse result))) (defun treesit-add-font-lock-rules (rules &optional how feature) @@ -1877,9 +1881,9 @@ If LOUDLY is non-nil, display some debugging information." ;; 1ms in xdisp.c, and 0.3ms in a small C file (for typing a single ;; character), not worth it. --yuan (dolist (setting treesit-font-lock-settings) - (let* ((query (nth 0 setting)) - (enable (nth 1 setting)) - (override (nth 3 setting)) + (let* ((query (treesit-font-lock-setting-query setting)) + (enable (treesit-font-lock-setting-enable setting)) + (override (treesit-font-lock-setting-override setting)) (language (treesit-query-language query)) (root-nodes (cl-remove-if-not (lambda (node)