diff options
| author | Theodor Thornhill | 2023-01-20 21:05:41 +0100 |
|---|---|---|
| committer | Theodor Thornhill | 2023-01-20 21:53:26 +0100 |
| commit | c6a7664f06710751b1122eff4492557ef5b2bfba (patch) | |
| tree | 19e05e60f8a5332d0c19551d35e93eeb7a4e5996 | |
| parent | 1fdd720b6b8e1858e9ab92b2f63cc473402e54d1 (diff) | |
| download | emacs-c6a7664f06710751b1122eff4492557ef5b2bfba.tar.gz emacs-c6a7664f06710751b1122eff4492557ef5b2bfba.zip | |
Add html-ts-mode (bug#60972)
* lisp/textmodes/html-ts-mode.el: New major mode for HTML support
powered by Tree-sitter.
* etc/NEWS: Mention it in NEWS.
| -rw-r--r-- | etc/NEWS | 7 | ||||
| -rw-r--r-- | lisp/textmodes/html-ts-mode.el | 137 |
2 files changed, 144 insertions, 0 deletions
| @@ -181,6 +181,13 @@ activate this behavior. | |||
| 181 | 181 | ||
| 182 | * New Modes and Packages in Emacs 30.1 | 182 | * New Modes and Packages in Emacs 30.1 |
| 183 | 183 | ||
| 184 | ** New major modes based on the tree-sitter library. | ||
| 185 | |||
| 186 | +++ | ||
| 187 | *** New major mode 'html-ts-mode'. | ||
| 188 | An optional major mode based on the tree-sitter library for editing | ||
| 189 | files written in HTML. | ||
| 190 | |||
| 184 | --- | 191 | --- |
| 185 | ** The highly accessible Modus themes collection has six items. | 192 | ** The highly accessible Modus themes collection has six items. |
| 186 | The 'modus-operandi' and 'modus-vivendi' are the main themes that have | 193 | The 'modus-operandi' and 'modus-vivendi' are the main themes that have |
diff --git a/lisp/textmodes/html-ts-mode.el b/lisp/textmodes/html-ts-mode.el new file mode 100644 index 00000000000..3f88a087163 --- /dev/null +++ b/lisp/textmodes/html-ts-mode.el | |||
| @@ -0,0 +1,137 @@ | |||
| 1 | ;;; html-ts-mode.el --- tree-sitter support for HTML -*- lexical-binding: t; -*- | ||
| 2 | |||
| 3 | ;; Copyright (C) 2023 Free Software Foundation, Inc. | ||
| 4 | |||
| 5 | ;; Author : Theodor Thornhill <theo@thornhill.no> | ||
| 6 | ;; Maintainer : Theodor Thornhill <theo@thornhill.no> | ||
| 7 | ;; Created : January 2023 | ||
| 8 | ;; Keywords : html languages tree-sitter | ||
| 9 | |||
| 10 | ;; This file is part of GNU Emacs. | ||
| 11 | |||
| 12 | ;; GNU Emacs is free software: you can redistribute it and/or modify | ||
| 13 | ;; it under the terms of the GNU General Public License as published by | ||
| 14 | ;; the Free Software Foundation, either version 3 of the License, or | ||
| 15 | ;; (at your option) any later version. | ||
| 16 | |||
| 17 | ;; GNU Emacs is distributed in the hope that it will be useful, | ||
| 18 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 19 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 20 | ;; GNU General Public License for more details. | ||
| 21 | |||
| 22 | ;; You should have received a copy of the GNU General Public License | ||
| 23 | ;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. | ||
| 24 | |||
| 25 | ;;; Commentary: | ||
| 26 | ;; | ||
| 27 | |||
| 28 | ;;; Code: | ||
| 29 | |||
| 30 | (require 'treesit) | ||
| 31 | (require 'sgml-mode) | ||
| 32 | |||
| 33 | (declare-function treesit-parser-create "treesit.c") | ||
| 34 | (declare-function treesit-node-type "treesit.c") | ||
| 35 | |||
| 36 | (defcustom html-ts-mode-indent-offset 2 | ||
| 37 | "Number of spaces for each indentation step in `html-ts-mode'." | ||
| 38 | :version "29.1" | ||
| 39 | :type 'integer | ||
| 40 | :safe 'integerp | ||
| 41 | :group 'html) | ||
| 42 | |||
| 43 | (defvar html-ts-mode--indent-rules | ||
| 44 | `((html | ||
| 45 | ((parent-is "fragment") parent-bol 0) | ||
| 46 | ((node-is "/>") parent-bol 0) | ||
| 47 | ((node-is ">") parent-bol 0) | ||
| 48 | ((node-is "end_tag") parent-bol 0) | ||
| 49 | ((parent-is "comment") prev-adaptive-prefix 0) | ||
| 50 | ((parent-is "element") parent-bol html-ts-mode-indent-offset) | ||
| 51 | ((parent-is "script_element") parent-bol html-ts-mode-indent-offset) | ||
| 52 | ((parent-is "style_element") parent-bol html-ts-mode-indent-offset) | ||
| 53 | ((parent-is "start_tag") parent-bol html-ts-mode-indent-offset) | ||
| 54 | ((parent-is "self_closing_tag") parent-bol html-ts-mode-indent-offset))) | ||
| 55 | "Tree-sitter indent rules.") | ||
| 56 | |||
| 57 | (defvar html-ts-mode--font-lock-settings | ||
| 58 | (treesit-font-lock-rules | ||
| 59 | :language 'html | ||
| 60 | :override t | ||
| 61 | :feature 'comment | ||
| 62 | `((comment) @font-lock-comment-face) | ||
| 63 | :language 'html | ||
| 64 | :override t | ||
| 65 | :feature 'keyword | ||
| 66 | `("doctype" @font-lock-keyword-face) | ||
| 67 | :language 'html | ||
| 68 | :override t | ||
| 69 | :feature 'definition | ||
| 70 | `((tag_name) @font-lock-function-name-face) | ||
| 71 | :language 'html | ||
| 72 | :override t | ||
| 73 | :feature 'string | ||
| 74 | `((quoted_attribute_value) @font-lock-string-face) | ||
| 75 | :language 'html | ||
| 76 | :override t | ||
| 77 | :feature 'property | ||
| 78 | `((attribute_name) @font-lock-variable-name-face)) | ||
| 79 | "Tree-sitter font-lock settings for `html-ts-mode'.") | ||
| 80 | |||
| 81 | (defun html-ts-mode--defun-name (node) | ||
| 82 | "Return the defun name of NODE. | ||
| 83 | Return nil if there is no name or if NODE is not a defun node." | ||
| 84 | (when (equal (treesit-node-type node) "tag_name") | ||
| 85 | (treesit-node-text node t))) | ||
| 86 | |||
| 87 | ;;;###autoload | ||
| 88 | (define-derived-mode html-ts-mode html-mode "HTML" | ||
| 89 | "Major mode for editing Html, powered by tree-sitter." | ||
| 90 | :group 'html | ||
| 91 | |||
| 92 | (unless (treesit-ready-p 'html) | ||
| 93 | (error "Tree-sitter for HTML isn't available")) | ||
| 94 | |||
| 95 | (treesit-parser-create 'html) | ||
| 96 | |||
| 97 | ;; Comments. | ||
| 98 | (setq-local treesit-text-type-regexp | ||
| 99 | (regexp-opt '("comment" "text"))) | ||
| 100 | |||
| 101 | ;; Indent. | ||
| 102 | (setq-local treesit-simple-indent-rules html-ts-mode--indent-rules) | ||
| 103 | |||
| 104 | ;; Navigation. | ||
| 105 | (setq-local treesit-defun-type-regexp "element") | ||
| 106 | |||
| 107 | (setq-local treesit-defun-name-function #'html-ts-mode--defun-name) | ||
| 108 | |||
| 109 | (setq-local treesit-sentence-type-regexp | ||
| 110 | (regexp-opt '("start_tag" | ||
| 111 | "self_closing_tag" | ||
| 112 | "end_tag"))) | ||
| 113 | |||
| 114 | (setq-local treesit-sexp-type-regexp | ||
| 115 | (regexp-opt '("tag" | ||
| 116 | "text" | ||
| 117 | "attribute" | ||
| 118 | "value"))) | ||
| 119 | |||
| 120 | ;; Font-lock. | ||
| 121 | (setq-local treesit-font-lock-settings html-ts-mode--font-lock-settings) | ||
| 122 | (setq-local treesit-font-lock-feature-list | ||
| 123 | '((comment keyword definition) | ||
| 124 | (property string) | ||
| 125 | () ())) | ||
| 126 | |||
| 127 | ;; Imenu. | ||
| 128 | (setq-local treesit-simple-imenu-settings | ||
| 129 | '(("Element" "\\`tag_name\\'" nil nil))) | ||
| 130 | (treesit-major-mode-setup)) | ||
| 131 | |||
| 132 | (if (treesit-ready-p 'html) | ||
| 133 | (add-to-list 'auto-mode-alist '("\\.html\\'" . html-ts-mode))) | ||
| 134 | |||
| 135 | (provide 'html-ts-mode) | ||
| 136 | |||
| 137 | ;;; html-ts-mode.el ends here | ||