Package: emacs;
Reported by: Vincenzo Pupillo <v.pupillo <at> gmail.com>
Date: Fri, 29 Nov 2024 21:58:01 UTC
Severity: wishlist
Fixed in version 31.0.50
Done: Juri Linkov <juri <at> linkov.net>
Bug is archived. No further changes may be made.
View this message in rfc822 format
From: Vincenzo Pupillo <v.pupillo <at> gmail.com> To: casouri <at> gmail.com, juri <at> linkov.net, Eli Zaretskii <eliz <at> gnu.org> Cc: 74610 <at> debbugs.gnu.org Subject: bug#74610: 31.0.50; Submitting mhtml-ts-mode, treesitter alternative to mhtml-mode Date: Sun, 09 Feb 2025 15:35:44 +0100
[Message part 1 (text/plain, inline)]
Ciao Eli, This attached patch is the third version. Adapted to support the latest changes introduced in treesit.el and js.el (as described in bug#75456). @Jury, @Yuan what do you think about this version? Thanks. Vincenzo In data sabato 8 febbraio 2025 10:01:48 Ora standard dell’Europa centrale, Eli Zaretskii ha scritto: > Ping! Can we please make progress with this issue? is the patch ready > to be installed? > > > From: Vincenzo Pupillo <v.pupillo <at> gmail.com> > > Cc: Juri Linkov <juri <at> linkov.net>, 74610 <at> debbugs.gnu.org, > > > > Eli Zaretskii <eliz <at> gnu.org> > > > > Date: Sun, 19 Jan 2025 18:09:31 +0100 > > > > Ciao Yuan, > > In data sabato 18 gennaio 2025 02:37:53 Ora standard dell’Europa centrale, > > > > Yuan Fu ha scritto: > > > > On Jan 14, 2025, at 1:41 PM, Vincenzo Pupillo <v.pupillo <at> gmail.com> > > > > wrote: > > > > > > > > Ciao Yuan and Juri, > > > > this is an updated version of mhtml-ts-mode. > > > > I have tried to reduce as much as possible copies of parts of the > > > > major > > > > modes from which it is derived. > > > > To do this, I had to move some values that were assigned directly to > > > > treesit's own variables (in ccs-mode.el, in js.el, and in > > > > html-ts-mode.el) into new variables. > > > > I also added three new functions to treesit.el to make it easier to > > > > combine > > > > parts derived from the other major modes. So now any changes to these > > > > new > > > > variables are directly reflected in the behavior of mhtml-ts-mode. > > > > There are a few things I would like to highlight: > > > > 1. treesit-font-lock-feature-lists are not defined per parser, so > > > > simply > > > > merging the different lists will cause display differences from the > > > > original major-modes; for example “function” is defined at level 3 in > > > > css-ts-mode but at level 4 in js-ts-mode. > > > > > > Yeah, that’s not pretty, I don’t have a good solution for now. > > > Technically a major mode can use treesit-font-lock-recompute-features > > > after > > > treesit-major-mode-setup to do fine adjustments, but it’s ugly. > > > > > > > 2. treesit-defun-type-regexp has the same problem as > > > > treesit-font-lock- > > > > feature-list, so I had to define it myself. > > > > > > Maybe you can use treesit-thing-settings instead? > > > > Ok, done. > > > > > > But other than that, it works pretty well. > > > > > > > > IMHO, a global "list" where you can define "font-lock", "indent-list", > > > > "font- lock-feature", etc. by language (perhaps with getter and setter > > > > methods) might make it easier to define new multilingual major-modes. > > > > It > > > > could improve the decoupling between multilingual major-modes and the > > > > major-modes they are derived from. It could also better decouple the > > > > internal implementation of treesit.el from the treesitter-based > > > > major-modes. > > > > > > > > Let me know what you think. > > > > > > That’s what I had in mind. That’s also why I added a new variable > > > treesit-aggregated-simple-imenu-settings instead of piggybacking on > > > treesit-simple-imenu-settings. A separate variable is simpler to borrow > > > settings from. > > > > Yes, in this new patch I used treesit-aggregated-simple-imenu-settings. > > > > > But I didn’t have time to think it though yet. (By language, or by mode > > > and > > > language? Should packages set it on load, or define a function that > > > loads > > > these settings? Etc.) > > > > I would say by mode and language, both mhtml-ts mode and html-ts mode have > > the same primary parser. For the second question, I don't know. I don't > > have enough experience with it to know what the pros and cons are. > > > > > Yuan > > > > Vincenzo > > > > From 199af27f1d761d100f6a8a6163c27e35e6ab2ea0 Mon Sep 17 00:00:00 2001 > > From: Vincenzo Pupillo <v.pupillo <at> gmail.com> > > Date: Sun, 19 Jan 2025 17:49:30 +0100 > > Subject: [PATCH] Add mhtml-ts-mode. > > > > New major-mode alternative to mhtml-mode, based on treesitter, for > > editing files containing html, javascript and css. > > > > * etc/NEWS: Mention the new mode and new functions. > > * lisp/textmodes/mhtml-ts-mode.el: New file. > > * lisp/progmodes/js.el (js--treesit-thing-settings): New variable. > > (js--treesit-font-lock-feature-list); New variable. > > (js--treesit-simple-imenu-settings): New variable. > > (js--treesit-defun-type-regexp): New variable. > > (js--treesit-jsdoc-comment-regexp): New variable. > > (js-ts-mode): Use of new variables instead of direct assignment of > > values. > > * lisp/textmodes/css-mode.el (css-mode--menu): New variable. > > (css-mode-map): Use new variable. > > (css--treesit-font-lock-feature-list): New variable. > > (css--treesit-simple-imenu-settings): New variable. > > (css--treesit-defun-type-regexp): New variable. > > (cs-ts-mode): Use of new variables instead of direct assignment of > > values. > > * lisp/textmodes/html-ts-mode.el > > (html-ts-mode--treesit-things-settings): New variable. > > (html-ts-mode--treesit-font-lock-feature-list): New variable. > > (html-ts-mode--treesit-simple-imenu-settings): New variable. > > (html-ts-mode--treesit-defun-type-regexp): New variable. > > (html-ts-mode): Use of new variables instead of direct assignment of > > values. > > * lisp/treesit.el > > (treesit-merge-font-lock-feature-list): New fuction. > > (treesit-replace-font-lock-feature-settings): New fuction. > > (treesit-modify-indent-rules): New function. > > --- > > > > etc/NEWS | 28 ++ > > lisp/progmodes/js.el | 72 ++-- > > lisp/textmodes/css-mode.el | 47 ++- > > lisp/textmodes/html-ts-mode.el | 45 ++- > > lisp/textmodes/mhtml-ts-mode.el | 600 ++++++++++++++++++++++++++++++++ > > lisp/treesit.el | 65 ++++ > > 6 files changed, 798 insertions(+), 59 deletions(-) > > create mode 100644 lisp/textmodes/mhtml-ts-mode.el > > > > diff --git a/etc/NEWS b/etc/NEWS > > index 0b849dec450..6360a082e9f 100644 > > --- a/etc/NEWS > > +++ b/etc/NEWS > > > > @@ -965,6 +965,12 @@ destination window is chosen using 'display-buffer- alist'. Example: > > > > * New Modes and Packages in Emacs 31.1 > > > > +** New major modes based on the tree-sitter library > > + > > +*** New major mode 'mhtml-ts-mode'. > > +An optional major mode based on the tree-sitter library for editing html > > +files. This mode handles indentation, fontification, and commenting for > > +embedded JavaScript and CSS. > > > > > > * Incompatible Lisp Changes in Emacs 31.1 > > > > @@ -1098,6 +1104,28 @@ language symbol. For example, 'cpp' is translated > > to "C++". A new> > > variable 'treesit-language-display-name-alist' holds the translations of > > language symbols where that translation is not trivial. > > > > ++++ > > +*** New function 'treesit-merge-font-lock-feature-list'. > > +This function the merge two tree-sitter font lock feature lists. > > +Returns a new font lock feature list with no duplicates in the same > > level. > > +It can be used to merge font lock feature lists in a multi-language major > > mode. + > > ++++ > > +*** New function 'treesit-replace-font-lock-feature-settings'. > > +Given two treesit-font-lock-settings replaces the feature in the second > > +font-lock-settings with the same feature in the first > > +font-lock-settings. In a multi-linguage major mode it is sometimes > > +necessary to replace features from one of the major modes, with others > > +that are better suited to the new multilingual context. > > + > > ++++ > > +*** New function 'treesit-modify-indent-rules'. > > +Given two treesit ident rules, it replaces, adds, or prepends the new > > +rules to the old ones, then returns a new treesit indent rules. > > +In a multi-linguage major mode it is sometimes necessary to modify rules > > +from one of the major modes, with others that are better suited to the > > +new multilingual context. > > + > > > > +++ > > *** New command 'treesit-explore' > > This command replaces 'treesit-explore-mode'. It turns on > > > > diff --git a/lisp/progmodes/js.el b/lisp/progmodes/js.el > > index 101b882c718..be9d6f64aef 100644 > > --- a/lisp/progmodes/js.el > > +++ b/lisp/progmodes/js.el > > @@ -3901,6 +3901,44 @@ js--treesit-list-nodes > > > > (defvar js--treesit-jsdoc-beginning-regexp (rx bos "/**") > > > > "Regular expression matching the beginning of a jsdoc block comment.") > > > > +(defvar js--treesit-thing-settings > > + `((javascript > > + (sexp ,(js--regexp-opt-symbol js--treesit-sexp-nodes)) > > + (list ,(js--regexp-opt-symbol js--treesit-list-nodes)) > > + (sentence ,(js--regexp-opt-symbol js--treesit-sentence-nodes)) > > + (text ,(js--regexp-opt-symbol '("comment" > > + "string_fragment"))))) > > + "Settings for `treesit-thing-settings'.") > > + > > +(defvar js--treesit-font-lock-feature-list > > + '(( comment document definition) > > + ( keyword string) > > + ( assignment constant escape-sequence jsx number > > + pattern string-interpolation) > > + ( bracket delimiter function operator property)) > > + "Settings for `treesit-font-lock-feature-list'.") > > + > > +(defvar js--treesit-simple-imenu-settings > > + `(("Function" "\\`function_declaration\\'" nil nil) > > + ("Variable" "\\`lexical_declaration\\'" > > + js--treesit-valid-imenu-entry nil) > > + ("Class" ,(rx bos (or "class_declaration" > > + "method_definition") > > + eos) > > + nil nil)) > > + "Settings for `treesit-simple-imenu'.") > > + > > +(defvar js--treesit-defun-type-regexp > > + (rx (or "class_declaration" > > + "method_definition" > > + "function_declaration" > > + "lexical_declaration")) > > + "Settings for `treesit-defun-type-regexp'.") > > + > > +(defvar js--treesit-jsdoc-comment-regexp > > + (rx (or "comment" "line_comment" "block_comment" "description")) > > + "Regexp for `c-ts-common--comment-regexp'.") > > + > > > > ;;;###autoload > > (define-derived-mode js-ts-mode js-base-mode "JavaScript" > > > > "Major mode for editing JavaScript. > > > > @@ -3931,29 +3969,15 @@ js-ts-mode > > > > ;; Indent. > > (setq-local treesit-simple-indent-rules js--treesit-indent-rules) > > ;; Navigation. > > > > - (setq-local treesit-defun-type-regexp > > - (rx (or "class_declaration" > > - "method_definition" > > - "function_declaration" > > - "lexical_declaration"))) > > + (setq-local treesit-defun-type-regexp js--treesit-defun-type-regexp) > > + > > > > (setq-local treesit-defun-name-function #'js--treesit-defun-name) > > > > - (setq-local treesit-thing-settings > > - `((javascript > > - (sexp ,(js--regexp-opt-symbol js--treesit-sexp-nodes)) > > - (list ,(js--regexp-opt-symbol js--treesit-list-nodes)) > > - (sentence ,(js--regexp-opt-symbol > > js--treesit-sentence-nodes)) - (text > > ,(js--regexp-opt-symbol '("comment" > > - > > "string_fragment")))))) > > + (setq-local treesit-thing-settings js--treesit-thing-settings) > > > > ;; Fontification. > > (setq-local treesit-font-lock-settings > > js--treesit-font-lock-settings) > > > > - (setq-local treesit-font-lock-feature-list > > - '(( comment document definition) > > - ( keyword string) > > - ( assignment constant escape-sequence jsx number > > - pattern string-interpolation) > > - ( bracket delimiter function operator property))) > > + (setq-local treesit-font-lock-feature-list > > js--treesit-font-lock-feature-list)> > > (when (treesit-ready-p 'jsdoc t) > > > > (setq-local treesit-range-settings > > > > @@ -3963,17 +3987,11 @@ js-ts-mode > > > > :local t > > > > `(((comment) @capture (:match > > ,js--treesit-jsdoc-beginning-regexp @capture)))))> > > - (setq c-ts-common--comment-regexp (rx (or "comment" "line_comment" > > "block_comment" "description")))) + (setq > > c-ts-common--comment-regexp js--treesit-jsdoc-comment-regexp))> > > ;; Imenu > > > > - (setq-local treesit-simple-imenu-settings > > - `(("Function" "\\`function_declaration\\'" nil nil) > > - ("Variable" "\\`lexical_declaration\\'" > > - js--treesit-valid-imenu-entry nil) > > - ("Class" ,(rx bos (or "class_declaration" > > - "method_definition") > > - eos) > > - nil nil))) > > + (setq-local treesit-simple-imenu-settings > > js--treesit-simple-imenu-settings) + > > > > (treesit-major-mode-setup) > > > > (add-to-list 'auto-mode-alist > > > > diff --git a/lisp/textmodes/css-mode.el b/lisp/textmodes/css-mode.el > > index 53340195386..35c61e4f66d 100644 > > --- a/lisp/textmodes/css-mode.el > > +++ b/lisp/textmodes/css-mode.el > > @@ -893,13 +893,7 @@ css-mode-syntax-table > > > > (modify-syntax-entry ?? "." st) > > st)) > > > > -(defvar-keymap css-mode-map > > - :doc "Keymap used in `css-mode'." > > - "<remap> <info-lookup-symbol>" #'css-lookup-symbol > > - ;; `info-complete-symbol' is not used. > > - "<remap> <complete-symbol>" #'completion-at-point > > - "C-c C-f" #'css-cycle-color-format > > - :menu > > +(defvar css-mode--menu > > > > '("CSS" > > > > :help "CSS-specific features" > > > > ["Reformat block" fill-paragraph > > > > @@ -910,7 +904,17 @@ css-mode-map > > > > ["Describe symbol" css-lookup-symbol > > > > :help "Display documentation for a CSS symbol"] > > > > ["Complete symbol" completion-at-point > > > > - :help "Complete symbol before point"])) > > + :help "Complete symbol before point"]) > > + "Menu bar for `css-mode'") > > + > > +(defvar-keymap css-mode-map > > + :doc "Keymap used in `css-mode'." > > + "<remap> <info-lookup-symbol>" #'css-lookup-symbol > > + ;; `info-complete-symbol' is not used. > > + "<remap> <complete-symbol>" #'completion-at-point > > + "C-c C-f" #'css-cycle-color-format > > + :menu > > + css-mode--menu) > > > > (eval-and-compile > > > > (defconst css--uri-re > > > > @@ -1771,6 +1775,21 @@ css--extract-index-name > > > > (replace-regexp-in-string "[\n ]+" " " s))) > > > > res))))))) > > > > +(defvar css--treesit-font-lock-feature-list > > + '((selector comment query keyword) > > + (property constant string) > > + (error variable function operator bracket)) > > + "Settings for `treesit-font-lock-feature-list'.") > > + > > +(defvar css--treesit-simple-imenu-settings > > + `(( nil ,(rx bos (or "rule_set" "media_statement") eos) > > + nil nil)) > > + "Settings for `treesit-simple-imenu'.") > > + > > +(defvar css--treesit-defun-type-regexp > > + "rule_set" > > + "Settings for `treesit-defun-type-regexp'.") > > + > > > > (define-derived-mode css-base-mode prog-mode "CSS" > > > > "Generic mode to edit Cascading Style Sheets (CSS). > > > > @@ -1825,16 +1844,12 @@ css-ts-mode > > > > ;; Tree-sitter specific setup. > > (setq treesit-primary-parser (treesit-parser-create 'css)) > > (setq-local treesit-simple-indent-rules css--treesit-indent-rules) > > > > - (setq-local treesit-defun-type-regexp "rule_set") > > + (setq-local treesit-defun-type-regexp css--treesit-defun-type-regexp) > > > > (setq-local treesit-defun-name-function #'css--treesit-defun-name) > > (setq-local treesit-font-lock-settings css--treesit-settings) > > > > - (setq-local treesit-font-lock-feature-list > > - '((selector comment query keyword) > > - (property constant string) > > - (error variable function operator bracket))) > > - (setq-local treesit-simple-imenu-settings > > - `(( nil ,(rx bos (or "rule_set" "media_statement") eos) > > - nil nil))) > > + (setq-local treesit-font-lock-feature-list > > css--treesit-font-lock-feature-list) + (setq-local > > treesit-simple-imenu-settings css--treesit-simple-imenu-settings) + > > > > (treesit-major-mode-setup) > > > > (add-to-list 'auto-mode-alist '("\\.css\\'" . css-ts-mode)))) > > > > diff --git a/lisp/textmodes/html-ts-mode.el > > b/lisp/textmodes/html-ts-mode.el index dad49b7ed4c..5700c2b406b 100644 > > --- a/lisp/textmodes/html-ts-mode.el > > +++ b/lisp/textmodes/html-ts-mode.el > > @@ -87,6 +87,31 @@ html-ts-mode--font-lock-settings > > > > `((attribute_name) @font-lock-variable-name-face)) > > > > "Tree-sitter font-lock settings for `html-ts-mode'.") > > > > +(defvar html-ts-mode--treesit-things-settings > > + `((html > > + (sexp ,(regexp-opt '("element" > > + "text" > > + "attribute" > > + "value"))) > > + (list ,(regexp-opt '("element")) 'symbols) > > + (sentence "tag") > > + (text ,(regexp-opt '("comment" "text"))))) > > + "Settings for `treesit-thing-settings'.") > > + > > +(defvar html-ts-mode--treesit-font-lock-feature-list > > + '((comment keyword definition) > > + (property string) > > + () ()) > > + "Settings for `treesit-font-lock-feature-list'.") > > + > > +(defvar html-ts-mode--treesit-simple-imenu-settings > > + '(("Element" "\\`tag_name\\'" nil nil)) > > + "Settings for `treesit-simple-imenu'.") > > + > > +(defvar html-ts-mode--treesit-defun-type-regexp > > + "element" > > + "Settings for `treesit-defun-type-regexp'.") > > + > > > > (defun html-ts-mode--defun-name (node) > > > > "Return the defun name of NODE. > > > > Return nil if there is no name or if NODE is not a defun node." > > > > @@ -107,30 +132,18 @@ html-ts-mode > > > > (setq-local treesit-simple-indent-rules html-ts-mode--indent-rules) > > > > ;; Navigation. > > > > - (setq-local treesit-defun-type-regexp "element") > > + (setq-local treesit-defun-type-regexp > > html-ts-mode--treesit-defun-type-regexp)> > > (setq-local treesit-defun-name-function #'html-ts-mode--defun-name) > > > > - (setq-local treesit-thing-settings > > - `((html > > - (sexp ,(regexp-opt '("element" > > - "text" > > - "attribute" > > - "value"))) > > - (list ,(regexp-opt '("element")) 'symbols) > > - (sentence "tag") > > - (text ,(regexp-opt '("comment" "text")))))) > > + (setq-local treesit-thing-settings > > html-ts-mode--treesit-things-settings)> > > ;; Font-lock. > > (setq-local treesit-font-lock-settings > > html-ts-mode--font-lock-settings) > > > > - (setq-local treesit-font-lock-feature-list > > - '((comment keyword definition) > > - (property string) > > - () ())) > > + (setq-local treesit-font-lock-feature-list > > html-ts-mode--treesit-font-lock-feature-list)> > > ;; Imenu. > > > > - (setq-local treesit-simple-imenu-settings > > - '(("Element" "\\`tag_name\\'" nil nil))) > > + (setq-local treesit-simple-imenu-settings > > html-ts-mode--treesit-simple-imenu-settings)> > > ;; Outline minor mode. > > (setq-local treesit-outline-predicate "\\`element\\'") > > > > diff --git a/lisp/textmodes/mhtml-ts-mode.el > > b/lisp/textmodes/mhtml-ts-mode.el new file mode 100644 > > index 00000000000..272d00a1ef6 > > --- /dev/null > > +++ b/lisp/textmodes/mhtml-ts-mode.el > > @@ -0,0 +1,600 @@ > > +;;; mhtml-ts-mode.el --- Major mode for HTML using tree-sitter -*- > > lexical-binding: t; -*- + > > +;; Copyright (C) 2024 Free Software Foundation, Inc. > > + > > +;; Author: Vincenzo Pupillo <v.pupillo <at> gmail.com> > > +;; Maintainer: Vincenzo Pupillo <v.pupillo <at> gmail.com> > > +;; Created: Nov 2024 > > +;; Keywords: HTML languages hypermedia tree-sitter > > + > > +;; This file is part of GNU Emacs. > > + > > +;; GNU Emacs is free software: you can redistribute it and/or modify > > +;; it under the terms of the GNU General Public License as published by > > +;; the Free Software Foundation, either version 3 of the License, or > > +;; (at your option) any later version. > > + > > +;; GNU Emacs is distributed in the hope that it will be useful, > > +;; but WITHOUT ANY WARRANTY; without even the implied warranty of > > +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > > +;; GNU General Public License for more details. > > + > > +;; You should have received a copy of the GNU General Public License > > +;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. > > + > > +;;; Commentary: > > +;; > > +;; This package provides `mhtml-ts-mode' which is a major mode > > +;; for editing HTML files with embedded JavaScript and CSS. > > +;; Tree Sitter is used to parse each of these languages. > > +;; > > +;; Please note that this package requires `html-ts-mode', which > > +;; registers itself as the major mode for editing HTML. > > +;; > > +;; This package is compatible and has been tested with the following > > +;; tree-sitter grammars: > > +;; * https://github.com/tree-sitter/tree-sitter-html > > +;; * https://github.com/tree-sitter/tree-sitter-javascript > > +;; * https://github.com/tree-sitter/tree-sitter-jsdoc > > +;; * https://github.com/tree-sitter/tree-sitter-css > > +;; > > +;; Features > > +;; > > +;; * Indent > > +;; * Flymake > > +;; * IMenu > > +;; * Navigation > > +;; * Which-function > > +;; * Tree-sitter parser installation helper > > + > > +;;; Code: > > + > > +(require 'treesit) > > +(require 'html-ts-mode) > > +(require 'css-mode) ;; for embed css into html > > +(require 'js) ;; for embed javascript into html > > + > > +(eval-when-compile > > + (require 'rx)) > > + > > +;; This tells the byte-compiler where the functions are defined. > > +;; Is only needed when a file needs to be able to byte-compile > > +;; in a Emacs not built with tree-sitter library. > > +(treesit-declare-unavailable-functions) > > + > > +;; In a multi-language major mode can be useful to have an "installer" to > > +;; simplify the installation of the grammars supported by the major-mode. > > +(defvar mhtml-ts-mode--language-source-alist > > + '((html . ("https://github.com/tree-sitter/tree-sitter-html" > > "v0.23.2")) + (javascript . > > ("https://github.com/tree-sitter/tree-sitter-javascript" "v0.23.1")) + > > (jsdoc . ("https://github.com/tree-sitter/tree-sitter-jsdoc" "v0.23.2")) > > + (css . ("https://github.com/tree-sitter/tree-sitter-css" > > "v0.23.1"))) + "Treesitter language parsers required by `mhtml-ts-mode'. > > +You can customize this variable if you want to stick to a specific > > +commit and/or use different parsers.") > > + > > +(defun mhtml-ts-mode-install-parsers () > > + "Install all the required treesitter parsers. > > +`mhtml-ts-mode--language-source-alist' defines which parsers to install." > > + (interactive) > > + (let ((treesit-language-source-alist > > mhtml-ts-mode--language-source-alist)) + (dolist (item > > mhtml-ts-mode--language-source-alist) > > + (treesit-install-language-grammar (car item))))) > > + > > +;;; Custom variables > > + > > +(defgroup mhtml-ts-mode nil > > + "Major mode for editing HTML files, based on `html-ts-mode'. > > +Works with JS and CSS and for that use `js-ts-mode' and `css-ts-mode'." > > + :prefix "mhtml-ts-mode-" > > + ;; :group 'languages > > + :group 'html) > > + > > +(defcustom mhtml-ts-mode-js-css-indent-offset 2 > > + "JavaScript and CSS indent spaces related to the <script> and <style> > > HTML tags. +By default should have same value as > > `html-ts-mode-indent-offset'." + :tag "HTML javascript or css indent > > offset" > > + :version "31.1" > > + :type 'integer > > + :safe 'integerp) > > + > > +(defcustom mhtml-ts-mode-pretty-print-command > > + ;; prefer tidy because it's used by sgml-mode > > + (let ((executable nil)) > > + (cond ((setq executable (executable-find "tidy")) > > + (format > > + "%s --gnu-emacs yes --wrap 0 --indent-spaces %s -q -i -" > > + executable html-ts-mode-indent-offset)) > > + ((setq executable (executable-find "xmllint")) > > + (format "%s --html --quiet --format -" executable)) > > + (t "Install tidy, ore some other HTML pretty print tool, and > > set `mhtml-ts-mode-pretty-print-command'."))) + "The command to pretty > > print the current HTML buffer." > > + :type 'string > > + :version "31.1") > > + > > +(defvar mhtml-ts-mode--js-css-indent-offset > > + mhtml-ts-mode-js-css-indent-offset > > + "Internal copy of `mhtml-ts-mode-js-css-indent-offset'. > > +The value changes, by `mhtml-ts-mode--tag-relative-indent-offset' > > according to +the value of `mhtml-ts-mode-tag-relative-indent'.") > > + > > +(defun mhtml-ts-mode--tag-relative-indent-offset (sym val) > > + "Custom setter for `mhtml-ts-mode-tag-relative-indent'. > > +Apart from setting the default value of SYM to VAL, also change the > > +value of SYM in `mhtml-ts-mode' buffers to VAL. SYM should be > > +`mhtml-ts-mode-tag-relative-indent', and VAL should be t, nil or > > +`ignore'. When sym is `mhtml-ts-mode-tag-relative-indent' set the > > +value of `mhtml-ts-mode--js-css-indent-offset' to 0 if VAL is t, > > +otherwise to `mhtml-ts-mode-js-css-indent-offset'." > > + (set-default sym val) > > + (when (eq sym 'mhtml-ts-mode-tag-relative-indent) > > + (setq > > + mhtml-ts-mode--js-css-indent-offset > > + (if (eq val t) > > + mhtml-ts-mode-js-css-indent-offset > > + 0)))) > > + > > +(defcustom mhtml-ts-mode-tag-relative-indent t > > + "How <script> and <style> bodies are indented relative to the tag. > > + > > +When t, indentation looks like: > > + > > + <script> > > + code(); > > + </script> > > + > > +When nil, indentation of the tag body starts just below the > > +tag, like: > > + > > + <script> > > + code(); > > + </script> > > + > > +When `ignore', the tag body starts in the first column, like: > > + > > + <script> > > +code(); > > + </script>" > > + :type '(choice (const nil) (const t) (const ignore)) > > + :safe 'symbolp > > + :set #'mhtml-ts-mode--tag-relative-indent-offset > > + :version "31.1") > > + > > +(defcustom mhtml-ts-mode-css-fontify-colors t > > + "Whether CSS colors should be fontified using the color as the > > background. +If non-nil, text representing a CSS color will be fontified > > +such that its background is the color itself. > > +Works like `css--fontify-region'." > > + :tag "HTML colors the CSS properties values." > > + :version "31.1" > > + :type 'boolean > > + :safe 'booleanp) > > + > > +(defvar mhtml-ts-mode-saved-pretty-print-command nil > > + "The command last used to pretty print in this buffer.") > > + > > +(defun mhtml-ts-mode-pretty-print (command) > > + "Prettify the current buffer. > > +Argument COMMAND The command to use." > > + (interactive > > + (list (read-string > > + "Prettify command: " > > + (or mhtml-ts-mode-saved-pretty-print-command > > + (concat mhtml-ts-mode-pretty-print-command " "))))) > > + (setq mhtml-ts-mode-saved-pretty-print-command command) > > + (save-excursion > > + (shell-command-on-region > > + (point-min) (point-max) > > + command (buffer-name) t > > + "*mhtml-ts-mode-pretty-pretty-print-errors*" t))) > > + > > +(defun mhtml-ts-mode--switch-fill-defun (&rest arguments) > > + "Switch between `fill-paragraph' and `prog-fill-reindent-defun'. > > +In an HTML region it calls `fill-paragraph' as does `html-ts-mode', > > +otherwise it calls `prog-fill-reindent-defun'. > > +Optional ARGUMENTS to to be passed to it." > > + (interactive) > > + (if (eq (treesit-language-at (point)) 'html) > > + (funcall-interactively #'fill-paragraph arguments) > > + (funcall-interactively #'prog-fill-reindent-defun arguments))) > > + > > +(defvar-keymap mhtml-ts-mode-map > > + :doc "Keymap for `mhtml-ts-mode' buffers." > > + :parent html-mode-map > > + ;; `mhtml-ts-mode' derive from `html-ts-mode' so the keymap is the > > + ;; same, we need to add some mapping from others languages. > > + "C-c C-f" #'css-cycle-color-format > > + "M-q" #'mhtml-ts-mode--switch-fill-defun) > > + > > +;; Place the CSS menu in the menu bar as well. > > +(easy-menu-define mhtml-ts-mode-menu mhtml-ts-mode-map > > + "Menu bar for `mhtml-ts-mode'." > > + css-mode--menu) > > + > > +;; Not used at the moment. > > +(defun mthml-ts-mode--js-language-at-point (point) > > + "Return the language at POINT assuming the point is within a Javascript > > region." + (let* ((node (treesit-node-at point 'javascript)) > > + (node-type (treesit-node-type node)) > > + (node-start (treesit-node-start node)) > > + (node-end (treesit-node-end node))) > > + (if (not (treesit-ready-p 'jsdoc t)) > > + 'javascript > > + (if (equal node-type "comment") > > + (save-excursion > > + ;; (message "node start = %s , end = %s" node-start node-end) > > + (goto-char node-start) > > + (if (search-forward "/**" node-end t) > > + 'jsdoc > > + 'javascript)) > > + 'javascript)))) > > + > > +;; To enable some basic treesiter functionality, you should define > > +;; a function that recognizes which grammar is used at-point. > > +;; This function should be assigned to > > `treesit-language-at-point-function' +(defun > > mhtml-ts-mode--language-at-point (point) > > + "Return the language at POINT assuming the point is within a HTML > > buffer." + (let* ((node (treesit-node-at point 'html)) > > + (parent (treesit-node-parent node)) > > + (node-query (format "(%s (%s))" > > + (treesit-node-type parent) > > + (treesit-node-type node)))) > > + (cond > > + ((equal "(script_element (raw_text))" node-query) 'javascript) > > + ;; ((equal "(script_element (raw_text))" node-query) > > 'mthml-ts-mode--js-language-at-point) + ((equal "(style_element > > (raw_text))" node-query) 'css) > > + (t 'html)))) > > + > > +;; Custom font-lock function that's used to apply color to css color > > +;; The signature of the function should be conforming to signature > > +;; QUERY-SPEC required by `treesit-font-lock-rules'. > > +(defun mhtml-ts-mode--colorize-css-value (node override start end &rest > > _) > > + "Colorize CSS property value like `css--fontify-region'. > > +For NODE, OVERRIDE, START, and END, see `treesit-font-lock-rules'." > > + (if (and mhtml-ts-mode-css-fontify-colors > > + (string-equal "plain_value" (treesit-node-type node))) > > + (let ((color (css--compute-color start (treesit-node-text node > > t)))) > > + (when color > > + (with-silent-modifications > > + (add-text-properties > > + (treesit-node-start node) (treesit-node-end node) > > + (list 'face (list :background color > > + :foreground (readable-foreground-color > > + color) > > + :box '(:line-width -1))))))) > > + (treesit-fontify-with-override > > + (treesit-node-start node) (treesit-node-end node) > > + 'font-lock-variable-name-face > > + override start end))) > > + > > +;; Embedded languages should be indented according to the language > > +;; that embeds them. > > +;; This function signature complies with `treesit-simple-indent-rules' > > +;; ANCHOR. > > +(defun mhtml-ts-mode--js-css-tag-bol (_node _parent &rest _) > > + "Find the first non-space characters of html tags <script> or <style>. > > +Return `line-beginning-position' when `treesit-node-at' is html, or > > +`mhtml-ts-mode-tag-relative-indent' is equal to ignore. > > +NODE and PARENT are ignored." > > + (if (or (eq (treesit-language-at (point)) 'html) > > + (eq mhtml-ts-mode-tag-relative-indent 'ignore)) > > + (line-beginning-position) > > + ;; Ok, we are in js or css block. > > + (save-excursion > > + (re-search-backward "<script.*>\\|<style.*>" nil t)))) > > + > > +;; Treesit supports 4 level of decoration, `treesit-font-lock-level' > > +;; define which level to use. Major modes categorize their fontification > > +;; features, these categories are defined by `treesit-font-lock-rules' of > > +;; each major-mode using :feature keyword. > > +;; In a multiple language Major mode it's a good idea to provide, for > > each > > +;; level, the union of the :feature of the same level. > > +;; TODO: Since the feature-list is not defined per "parser" (like, for > > +;; example, the thing-settings), the same feature can appear in > > +;; different levels, so the appearance of a multiple main mode can be > > +;; different from the main mode used. For e.g the feature "function" is > > +;; at level 4 for Javascript while it is at level 3 for CSS. > > +(defvar mhtml-ts-mode--treesit-font-lock-feature-list > > + (treesit-merge-font-lock-feature-list > > + html-ts-mode--treesit-font-lock-feature-list > > + (treesit-merge-font-lock-feature-list > > + js--treesit-font-lock-feature-list > > + css--treesit-font-lock-feature-list)) > > + "Settings for `treesit-font-lock-feature-list'.") > > + > > +(defvar mhtml-ts-mode--treesit-font-lock-settings > > + (append html-ts-mode--font-lock-settings > > + js--treesit-font-lock-settings > > + ;; Let's replace a css rule with a new one that adds color to > > + ;; the css value. > > + (treesit-replace-font-lock-feature-settings > > + (treesit-font-lock-rules > > + :language 'css > > + :override t > > + :feature 'variable > > + '((plain_value) @font-lock-variable-name-face > > + (plain_value) @mhtml-ts-mode--colorize-css-value)) > > + css--treesit-settings)) > > + "Settings for `treesit-font-lock-settings'.") > > + > > +(defvar mhtml-ts-mode--treesit-thing-settings > > + ;; In addition to putting together the various definitions, we need to > > + ;; add 'defun' which is used to support `imenu' and 'which-function'. > > + (list > > + ;; HTML thing settings > > + (append > > + (car html-ts-mode--treesit-things-settings) > > + `((defun ,(regexp-opt (list > > html-ts-mode--treesit-defun-type-regexp))))) + ;; Javascript thing > > settings > > + (append > > + (car js--treesit-thing-settings) > > + `((defun ,js--treesit-defun-type-regexp))) > > + ;; CSS thing settings > > + `(css > > + (defun ,(regexp-opt (list css--treesit-defun-type-regexp))))) > > + "Settings for `treesit-thing-settings'.") > > + > > +(defvar mhtml-ts-mode--treesit-indent-rules > > + (treesit--indent-rules-optimize > > + (append html-ts-mode--indent-rules > > + ;; Extended rules for js and css, to > > + ;; indent appropriately when injected > > + ;; into html > > + (treesit-modify-indent-rules > > + `((javascript ((parent-is "program") > > + mhtml-ts-mode--js-css-tag-bol > > + mhtml-ts-mode--js-css-indent-offset))) > > + js--treesit-indent-rules > > + :replace) > > + (treesit-modify-indent-rules > > + `((css ((parent-is "stylesheet") > > + mhtml-ts-mode--js-css-tag-bol > > + mhtml-ts-mode--js-css-indent-offset))) > > + css--treesit-indent-rules 'prepend) > > + :replace)) > > + "Settings for `treesit-simple-indent-rules'.") > > + > > +(defvar mhtml-ts-mode--treesit-aggregated-simple-imenu-settings > > + `((html ,@html-ts-mode--treesit-simple-imenu-settings) > > + (javascript ,@js--treesit-simple-imenu-settings) > > + (css ,@css--treesit-simple-imenu-settings)) > > + "Settings for `treesit-simple-imenu'.") > > + > > +;; TODO: treesit-defun-type-regexp should have an aggregated version, > > +;; like treesit-aggregated-simple-imenu-settings. Otherwise we can't > > +;; reuse the regex defined in the major mode we use. > > +(defvar mhtml-ts-mode--treesit-defun-type-regexp > > + (regexp-opt '("class_declaration" > > + "method_definition" > > + "function_declaration" > > + "lexical_declaration" > > + "element" > > + "rule_set")) > > + "Settings for `treesit-defun-type-regexp'.") > > + > > +;; In order to support `prettify-symbols-mode', just `append' the > > prettify > > +;; alist of all the languages. In our case only javascript defined this > > alist. +(defvar mhtml-ts-mode--prettify-symbols-alist > > js--prettify-symbols-alist + "Alist of symbol prettifications for > > various supported languages.") + > > +;; In order to support `which-fuction-mode' we should define > > +;; a function that return the defun name. > > +;; In a multilingual treesit mode, this can be implemented simply by > > +;; calling language-specific functions. > > +(defun mhtml-ts-mode--defun-name (node) > > + "Return the defun name of NODE. > > +Return nil if there is no name or if NODE is not a defun node." > > + (let ((html-name (html-ts-mode--defun-name node)) > > + (js-name (js--treesit-defun-name node)) > > + (css-name (css--treesit-defun-name node))) > > + (cond > > + (html-name html-name) > > + (js-name js-name) > > + (css-name css-name)))) > > + > > +;;; Flymake integration > > + > > +(defvar-local mhtml-ts-mode--flymake-process nil > > + "Store the Flymake process.") > > + > > +(defun mhtml-ts-mode-flymake-mhtml (report-fn &rest _args) > > + "MHTML backend for Flymake. > > +Calls REPORT-FN directly. Requires tidy." > > + (when (process-live-p mhtml-ts-mode--flymake-process) > > + (kill-process mhtml-ts-mode--flymake-process)) > > + (let ((tidy (executable-find "tidy")) > > + (source (current-buffer)) > > + (diagnostics-pattern (eval-when-compile > > + (rx bol > > + "line " (group (+ num)) ;; :1 line > > + " column " (group (+ num)) ;; :2 > > column > > + " - " (group (+? nonl)) ;; :3 type > > + ": " (group (+? nonl)) ;; :4 msg > > + eol)))) > > + (if (not tidy) > > + (error "Unable to find tidy command") > > + (save-restriction > > + (widen) > > + (setq mhtml-ts-mode--flymake-process > > + (make-process > > + :name "mhtml-ts-mode-flymake" > > + :noquery t > > + :connection-type 'pipe > > + :buffer (generate-new-buffer "*mhtml-ts-mode-flymake*") > > + :command `(,tidy "--gnu-emacs" "yes" "-e" "-q") > > + :sentinel > > + (lambda (proc _event) > > + (when (eq 'exit (process-status proc)) > > + (unwind-protect > > + (if (with-current-buffer source > > + (eq proc mhtml-ts-mode--flymake-process)) > > + (with-current-buffer (process-buffer proc) > > + (goto-char (point-min)) > > + (let (diags) > > + (while (search-forward-regexp > > diagnostics-pattern nil t) + (let* ((pos > > + (flymake-diag-region > > + source > > + (string-to-number (match-string > > 1)) + (string-to-number > > (match-string 2)))) ;; line and column + > > (type (cond ((equal (match-string 3) "Warning") :warning) + > > ((equal (match-string 3) > > "Error") :error))) ;; type of message + > > (msg (match-string 4))) ;; message + > > (push (flymake-make-diagnostic source (car pos) (cdr pos) type msg) + > > diags))) > > + (funcall report-fn diags))) > > + (flymake-log :warning "Canceling obsolete check > > %s" proc)) + (kill-buffer (process-buffer proc))))))) > > + (process-send-region mhtml-ts-mode--flymake-process (point-min) > > (point-max)) + (process-send-eof > > mhtml-ts-mode--flymake-process))))) > > + > > +(define-derived-mode mhtml-ts-mode html-ts-mode > > + '("HTML+" (:eval (let ((lang (mhtml-ts-mode--language-at-point > > (point)))) + (cond ((eq lang 'html) "") > > + ((eq lang 'javascript) "JS") > > + ((eq lang 'css) "CSS"))))) > > + "Major mode for editing HTML with embedded JavaScript and CSS. > > +Powered by tree-sitter." > > + (if (not (and > > + (treesit-ready-p 'html) > > + (treesit-ready-p 'javascript) > > + (treesit-ready-p 'css))) > > + (error "Tree-sitter parsers for HTML isn't available. You can > > + install the parsers with M-x `mhtml-ts-mode-install-parsers'") > > + > > + ;; When an language is embedded, you should initialize some variable > > + ;; just like it's done in the original mode. > > + > > + ;; Comment. > > + ;; indenting settings for js-ts-mode. > > + (c-ts-common-comment-setup) > > + (setq-local comment-multi-line t) > > + > > + ;; Font-lock. > > + > > + ;; There are two ways to handle embedded code: > > + ;; 1. Use a single parser for all the embedded code in the buffer. In > > + ;; this case, the embedded code blocks are concatenated together and > > are + ;; seen as a single continuous document to the parser. > > + ;; 2. Each embedded code block gets its own parser. Each parser only > > sees + ;; that particular code block. > > + > > + ;; If you go with 2 for a language, the local parsers are created and > > + ;; destroyed automatically by Emacs. So don't create a global parser > > for + ;; that embedded language here. > > + > > + ;; Create the parsers, only the global ones. > > + ;; jsdoc is a local parser, don't create a parser for it. > > + (treesit-parser-create 'css) > > + (treesit-parser-create 'javascript) > > + > > + ;; Multi-language modes must set the primary parser. > > + (setq-local treesit-primary-parser (treesit-parser-create 'html)) > > + > > + (setq-local treesit-range-settings > > + (treesit-range-rules > > + :embed 'javascript > > + :host 'html > > + '((script_element > > + (start_tag (tag_name)) > > + (raw_text) @cap)) > > + > > + ;; Another rule could be added that when it matches an > > + ;; attribute_value that has as its parent an > > + ;; attribute_name "style" it captures it and then > > + ;; passes it to the css parser. > > + :embed 'css > > + :host 'html > > + '((style_element > > + (start_tag (tag_name)) > > + (raw_text) @cap)))) > > + > > + ;; jsdoc is not mandatory for js-ts-mode, so we respect this by > > + ;; adding jsdoc range rules only when jsdoc is available. > > + (when (treesit-ready-p 'jsdoc t) > > + (setq-local treesit-range-settings > > + (append treesit-range-settings > > + (treesit-range-rules > > + :embed 'jsdoc > > + :host 'javascript > > + :local t > > + `(((comment) @cap > > + (:match ,js--treesit-jsdoc-beginning-regexp > > @cap)))))) + (setq-local c-ts-common--comment-regexp > > + js--treesit-jsdoc-comment-regexp)) > > + > > + > > + ;; Many treesit fuctions need to know the language at-point. > > + ;; So you should define such a function. > > + (setq-local treesit-language-at-point-function > > #'mhtml-ts-mode--language-at-point) + (setq-local > > prettify-symbols-alist mhtml-ts-mode--prettify-symbols-alist) + > > + ;; Indent. > > + > > + ;; Since `mhtml-ts-mode' inherits indentation rules from > > `html-ts-mode', `js-ts-mode' + ;; and `css-ts-mode', if you want to > > change the offset you have to act on the + ;; *-offset variables > > defined for those languages. > > + > > + ;; JavaScript and CSS must be indented relative to their code block. > > + ;; This is done by inserting a special rule before the normal > > + ;; indentation rules of these languages. > > + ;; The value of `mhtml-ts-mode--js-css-indent-offset' changes based > > on > > + ;; `mhtml-ts-mode-tag-relative-indent' and can be used to indent > > + ;; JavaScript and CSS code relative to the HTML that contains them, > > + ;; just like in mhtml-mode. > > + (setq-local treesit-simple-indent-rules > > mhtml-ts-mode--treesit-indent-rules) + > > + ;; Navigation. > > + > > + ;; This is for which-function-mode. > > + ;; Since mhtml-ts-mode is derived from html-ts-mode, which sets > > + ;; the value of `treesit-defun-type-regexp', you have to reset it to > > nil + ;; otherwise `imenu' and `which-function-mode' will not work. + > > (setq-local treesit-defun-type-regexp nil) > > + > > + ;; This is for finding defun name, it's used by IMenu as default > > + ;; function no specific functions are defined. > > + (setq-local treesit-defun-name-function #'mhtml-ts-mode--defun-name) > > + > > + ;; Define what are 'thing' for treesit. > > + ;; 'Thing' is a symbol representing the thing, like `defun', `sexp', > > or + ;; `sentence'. > > + ;; As an alternative, if you want just defun, you can define a > > `treesit-defun-type-regexp'. + (setq-local treesit-thing-settings > > mhtml-ts-mode--treesit-thing-settings) + > > + ;; Font-lock. > > + > > + ;; In a multi-language scenario, font lock settings are usually a > > + ;; concatenation of language rules. As you can see, it is possible > > + ;; to extend/modify the default rule or use a different set of > > + ;; rules. See `php-ts-mode--custom-html-font-lock-settings' for more > > + ;; advanced usage. > > + (setq-local treesit-font-lock-settings > > mhtml-ts-mode--treesit-font-lock-settings) + > > + ;; Tells treesit the list of features to fontify. > > + (setq-local treesit-font-lock-feature-list > > mhtml-ts-mode--treesit-font-lock-feature-list) + > > + ;; Imenu > > + > > + ;; Setup Imenu: if no function is specified, try to find an object > > + ;; using `treesit-defun-name-function'. > > + (setq-local treesit-aggregated-simple-imenu-settings > > + mhtml-ts-mode--treesit-aggregated-simple-imenu-settings) > > + > > + (treesit-major-mode-setup) > > + > > + ;; This is sort of a prog-mode as well as a text mode. > > + (run-mode-hooks 'prog-mode-hook) > > + > > + ;; Flymake > > + (add-hook 'flymake-diagnostic-functions #'mhtml-ts-mode-flymake-mhtml > > nil 'local))) + > > +;; Add nome extra parents. > > +(derived-mode-add-parents 'mhtml-ts-mode '(css-mode js-mode)) > > + > > +(when (and (treesit-ready-p 'html) (treesit-ready-p 'javascript) > > (treesit-ready-p 'css)) + (add-to-list > > + 'auto-mode-alist '("\\.[sx]?html?\\(\\.[a-zA-Z_]+\\)?\\'" . > > mhtml-ts-mode))) + > > +(provide 'mhtml-ts-mode) > > +;;; mhtml-ts-mode.el ends here > > diff --git a/lisp/treesit.el b/lisp/treesit.el > > index 8d86d142e3f..00b7d266e74 100644 > > --- a/lisp/treesit.el > > +++ b/lisp/treesit.el > > @@ -1297,6 +1297,40 @@ treesit-font-lock-recompute-features > > > > ((memq feature remove-list) nil) > > (t current-value)))))) > > > > +(defun treesit-merge-font-lock-feature-list (features-list-1 > > features-list-2) + "Merge two tree-sitter font lock feature lists. > > +Returns a new font lock feature list with no duplicates in the same > > level. > > +It can be used to merge font lock feature lists in a multi-language major > > mode. +FEATURES-LIST-1 and FEATURES-LIST-2 are list of lists of feature > > symbols." + (let ((result nil) > > + (features-1 (car features-list-1)) > > + (features-2 (car features-list-2))) > > + (while (or features-1 features-2) > > + (cond > > + ((and features-1 (not features-2)) (push features-1 result)) > > + ((and (not features-1) features-2) (push features-2 result)) > > + ((and features-1 features-2) (push (cl-union features-1 > > features-2) result))) + (setq features-list-1 (cdr features-list-1) > > + features-list-2 (cdr features-list-2) > > + features-1 (car features-list-1) > > + features-2 (car features-list-2))) > > + (nreverse result))) > > + > > +(defun treesit-replace-font-lock-feature-settings (new-settings settings) > > + "Replaces :feature in SETTINGS with :feature from NEW-SETTINGS. > > +Both SETTINGS and NEW-SETTINGS must be a value suitable for > > +`treesit-font-lock-settings'. > > +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)))))) > > + (nreverse result))) > > + > > > > (defun treesit-add-font-lock-rules (rules &optional how feature) > > > > "Add font-lock RULES to the current buffer. > > > > @@ -2401,6 +2435,37 @@ treesit--indent-rules-optimize > > > > offset))))) > > > > (cons lang (mapcar #'optimize-rule indent-rules))))) > > > > +(defun treesit-modify-indent-rules (new-rules rules &optional how) > > + "Modify RULES using NEW-RULES. > > +As default replace rules with the same anchor. > > +When HOW is :prepend NEW-RULES are prepend to RULES, when > > +:append NEW-RULES are appended to RULES, when :replace (the default) > > +NEW-RULES replace rule in RULES which the same anchor." > > + (let ((n-lang (car (car new-rules))) > > + (lang (car (car rules)))) > > + (when (not (eq n-lang lang)) > > + (error "The language must be the same")) > > + (let* ((nr (cdr (car new-rules))) > > + (r (cdr (car rules))) > > + (tmp nil) > > + (result > > + (cond > > + ((eq how :prepend) > > + (append nr r)) > > + ((eq how :append) > > + (append r nr)) > > + ((or (eq how :replace) t) > > + (nreverse > > + (progn > > + (dolist (new-rule nr) > > + (dolist (rule r) > > + (if (equal (nth 0 new-rule) (nth 0 rule)) > > + (push new-rule tmp) > > + (push rule tmp)))) > > + tmp)))))) > > + (push lang result) > > + `(,result)))) > > + > > > > ;;; Search > > > > (defun treesit-search-forward-goto
[0001-Add-mhtml-ts-mode.patch (text/x-patch, attachment)]
GNU bug tracking system
Copyright (C) 1999 Darren O. Benham,
1997,2003 nCipher Corporation Ltd,
1994-97 Ian Jackson.