diff options
| author | Tom Tromey | 2017-03-23 11:34:18 -0600 |
|---|---|---|
| committer | Tom Tromey | 2017-04-05 15:53:39 -0600 |
| commit | 335174ee5037a2751c31bfd9ecb87cedb4bc3cda (patch) | |
| tree | d2faaab5f48303aa08ef707d842d2e22e31650f6 | |
| parent | d392b6e82460d94b11627998da87e33880664060 (diff) | |
| download | emacs-335174ee5037a2751c31bfd9ecb87cedb4bc3cda.tar.gz emacs-335174ee5037a2751c31bfd9ecb87cedb4bc3cda.zip | |
add mhtml-mode.el
* etc/NEWS: Update.
* lisp/textmodes/mhtml-mode.el: New file.
* test/manual/indent/html-multi.html: New file.
* test/lisp/textmodes/mhtml-mode-tests.el: New file.
* doc/emacs/text.texi (HTML Mode): Mention mhtml-mode.
| -rw-r--r-- | doc/emacs/text.texi | 10 | ||||
| -rw-r--r-- | etc/NEWS | 7 | ||||
| -rw-r--r-- | lisp/textmodes/mhtml-mode.el | 390 | ||||
| -rw-r--r-- | test/lisp/textmodes/mhtml-mode-tests.el | 58 | ||||
| -rw-r--r-- | test/manual/indent/html-multi.html | 30 |
5 files changed, 493 insertions, 2 deletions
diff --git a/doc/emacs/text.texi b/doc/emacs/text.texi index 5f02d0b6920..d1e451175ed 100644 --- a/doc/emacs/text.texi +++ b/doc/emacs/text.texi | |||
| @@ -1899,8 +1899,14 @@ between Latin-1 encoded files and @TeX{}-encoded equivalents. | |||
| 1899 | @findex html-mode | 1899 | @findex html-mode |
| 1900 | 1900 | ||
| 1901 | The major modes for SGML and HTML provide indentation support and | 1901 | The major modes for SGML and HTML provide indentation support and |
| 1902 | commands for operating on tags. HTML mode is a slightly customized | 1902 | commands for operating on tags. |
| 1903 | variant of SGML mode. | 1903 | |
| 1904 | HTML consists of two modes---one, a basic mode called | ||
| 1905 | @code{html-mode} is a slightly customized variant of SGML mode. The | ||
| 1906 | other, which is used by default for HTML files, is called | ||
| 1907 | @code{mhtml-mode}, and attempts to properly handle Javascript enclosed | ||
| 1908 | in a @code{<script>} element and CSS embedded in a @code{<style>} | ||
| 1909 | element. | ||
| 1904 | 1910 | ||
| 1905 | @table @kbd | 1911 | @table @kbd |
| 1906 | @item C-c C-n | 1912 | @item C-c C-n |
| @@ -753,6 +753,13 @@ processes on exit. | |||
| 753 | 753 | ||
| 754 | ** New library 'xdg' with utilities for some XDG standards and specs. | 754 | ** New library 'xdg' with utilities for some XDG standards and specs. |
| 755 | 755 | ||
| 756 | ** HTML | ||
| 757 | |||
| 758 | +++ | ||
| 759 | *** A new submode of `html-mode', `mthml-mode', is now the default | ||
| 760 | mode for *.html files. This mode handles indentation, | ||
| 761 | fontification, and commenting for embedded JavaScript and CSS. | ||
| 762 | |||
| 756 | 763 | ||
| 757 | * Incompatible Lisp Changes in Emacs 26.1 | 764 | * Incompatible Lisp Changes in Emacs 26.1 |
| 758 | 765 | ||
diff --git a/lisp/textmodes/mhtml-mode.el b/lisp/textmodes/mhtml-mode.el new file mode 100644 index 00000000000..e9e09d4d959 --- /dev/null +++ b/lisp/textmodes/mhtml-mode.el | |||
| @@ -0,0 +1,390 @@ | |||
| 1 | ;;; mhtml-mode.el --- HTML editing mode that handles CSS and JS -*- lexical-binding:t -*- | ||
| 2 | |||
| 3 | ;; Copyright (C) 2017 Free Software Foundation, Inc. | ||
| 4 | |||
| 5 | ;; Keywords: wp, hypermedia, comm, languages | ||
| 6 | |||
| 7 | ;; This file is part of GNU Emacs. | ||
| 8 | |||
| 9 | ;; GNU Emacs is free software: you can redistribute it and/or modify | ||
| 10 | ;; it under the terms of the GNU General Public License as published by | ||
| 11 | ;; the Free Software Foundation, either version 3 of the License, or | ||
| 12 | ;; (at your option) any later version. | ||
| 13 | |||
| 14 | ;; GNU Emacs is distributed in the hope that it will be useful, | ||
| 15 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 16 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 17 | ;; GNU General Public License for more details. | ||
| 18 | |||
| 19 | ;; You should have received a copy of the GNU General Public License | ||
| 20 | ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. | ||
| 21 | |||
| 22 | ;;; Code: | ||
| 23 | |||
| 24 | (eval-and-compile | ||
| 25 | (require 'flyspell) | ||
| 26 | (require 'sgml-mode)) | ||
| 27 | (require 'js) | ||
| 28 | (require 'css-mode) | ||
| 29 | (require 'prog-mode) | ||
| 30 | (require 'font-lock) | ||
| 31 | |||
| 32 | (defcustom mhtml-tag-relative-indent t | ||
| 33 | "How <script> and <style> bodies are indented relative to the tag. | ||
| 34 | |||
| 35 | When t, indentation looks like: | ||
| 36 | |||
| 37 | <script> | ||
| 38 | code(); | ||
| 39 | </script> | ||
| 40 | |||
| 41 | When nil, indentation of the script body starts just below the | ||
| 42 | tag, like: | ||
| 43 | |||
| 44 | <script> | ||
| 45 | code(); | ||
| 46 | </script> | ||
| 47 | |||
| 48 | When `ignore', the script body starts in the first column, like: | ||
| 49 | |||
| 50 | <script> | ||
| 51 | code(); | ||
| 52 | </script>" | ||
| 53 | :group 'sgml | ||
| 54 | :type '(choice (const nil) (const t) (const ignore)) | ||
| 55 | :safe 'symbolp | ||
| 56 | :version "26.1") | ||
| 57 | |||
| 58 | (cl-defstruct mhtml--submode | ||
| 59 | ;; Name of this submode. | ||
| 60 | name | ||
| 61 | ;; HTML end tag. | ||
| 62 | end-tag | ||
| 63 | ;; Syntax table. | ||
| 64 | syntax-table | ||
| 65 | ;; Propertize function. | ||
| 66 | propertize | ||
| 67 | ;; Keymap. | ||
| 68 | keymap | ||
| 69 | ;; Captured locals that are set when entering a region. | ||
| 70 | crucial-captured-locals | ||
| 71 | ;; Other captured local variables; these are not set when entering a | ||
| 72 | ;; region but let-bound during certain operations, e.g., | ||
| 73 | ;; indentation. | ||
| 74 | captured-locals) | ||
| 75 | |||
| 76 | (defconst mhtml--crucial-variable-prefix | ||
| 77 | (regexp-opt '("comment-" "uncomment-" "electric-indent-" | ||
| 78 | "smie-" "forward-sexp-function")) | ||
| 79 | "Regexp matching the prefix of \"crucial\" buffer-locals we want to capture.") | ||
| 80 | |||
| 81 | (defconst mhtml--variable-prefix | ||
| 82 | (regexp-opt '("font-lock-" "indent-line-function" "major-mode")) | ||
| 83 | "Regexp matching the prefix of buffer-locals we want to capture.") | ||
| 84 | |||
| 85 | (defun mhtml--construct-submode (mode &rest args) | ||
| 86 | "A wrapper for make-mhtml--submode that computes the buffer-local variables." | ||
| 87 | (let ((captured-locals nil) | ||
| 88 | (crucial-captured-locals nil) | ||
| 89 | (submode (apply #'make-mhtml--submode args))) | ||
| 90 | (with-temp-buffer | ||
| 91 | (funcall mode) | ||
| 92 | ;; Make sure font lock is all set up. | ||
| 93 | (font-lock-set-defaults) | ||
| 94 | ;; This has to be set to a value other than the mthml-mode | ||
| 95 | ;; value, to avoid recursion. | ||
| 96 | (unless (variable-binding-locus 'font-lock-fontify-region-function) | ||
| 97 | (setq-local font-lock-fontify-region-function | ||
| 98 | #'font-lock-default-fontify-region)) | ||
| 99 | (dolist (iter (buffer-local-variables)) | ||
| 100 | (when (string-match mhtml--crucial-variable-prefix | ||
| 101 | (symbol-name (car iter))) | ||
| 102 | (push iter crucial-captured-locals)) | ||
| 103 | (when (string-match mhtml--variable-prefix (symbol-name (car iter))) | ||
| 104 | (push iter captured-locals))) | ||
| 105 | (setf (mhtml--submode-crucial-captured-locals submode) | ||
| 106 | crucial-captured-locals) | ||
| 107 | (setf (mhtml--submode-captured-locals submode) captured-locals)) | ||
| 108 | submode)) | ||
| 109 | |||
| 110 | (defun mhtml--mark-buffer-locals (submode) | ||
| 111 | (dolist (iter (mhtml--submode-captured-locals submode)) | ||
| 112 | (make-local-variable (car iter)))) | ||
| 113 | |||
| 114 | (defvar-local mhtml--crucial-variables nil | ||
| 115 | "List of all crucial variable symbols.") | ||
| 116 | |||
| 117 | (defun mhtml--mark-crucial-buffer-locals (submode) | ||
| 118 | (dolist (iter (mhtml--submode-crucial-captured-locals submode)) | ||
| 119 | (make-local-variable (car iter)) | ||
| 120 | (push (car iter) mhtml--crucial-variables))) | ||
| 121 | |||
| 122 | (defconst mhtml--css-submode | ||
| 123 | (mhtml--construct-submode 'css-mode | ||
| 124 | :name "CSS" | ||
| 125 | :end-tag "</style>" | ||
| 126 | :syntax-table css-mode-syntax-table | ||
| 127 | :propertize css-syntax-propertize-function | ||
| 128 | :keymap css-mode-map)) | ||
| 129 | |||
| 130 | (defconst mhtml--js-submode | ||
| 131 | (mhtml--construct-submode 'js-mode | ||
| 132 | :name "JS" | ||
| 133 | :end-tag "</script>" | ||
| 134 | :syntax-table js-mode-syntax-table | ||
| 135 | :propertize #'js-syntax-propertize | ||
| 136 | :keymap js-mode-map)) | ||
| 137 | |||
| 138 | (defmacro mhtml--with-locals (submode &rest body) | ||
| 139 | (declare (indent 1)) | ||
| 140 | `(cl-progv | ||
| 141 | (when ,submode (mapcar #'car (mhtml--submode-captured-locals ,submode))) | ||
| 142 | (when ,submode (mapcar #'cdr (mhtml--submode-captured-locals ,submode))) | ||
| 143 | (cl-progv | ||
| 144 | (when ,submode (mapcar #'car (mhtml--submode-crucial-captured-locals | ||
| 145 | ,submode))) | ||
| 146 | (when ,submode (mapcar #'cdr (mhtml--submode-crucial-captured-locals | ||
| 147 | ,submode))) | ||
| 148 | ,@body))) | ||
| 149 | |||
| 150 | (defun mhtml--submode-lighter () | ||
| 151 | "Mode-line lighter indicating the current submode." | ||
| 152 | (let ((submode (get-text-property (point) 'mhtml-submode))) | ||
| 153 | (if submode | ||
| 154 | (mhtml--submode-name submode) | ||
| 155 | ""))) | ||
| 156 | |||
| 157 | (defvar font-lock-beg) | ||
| 158 | (defvar font-lock-end) | ||
| 159 | |||
| 160 | (defun mhtml--extend-font-lock-region () | ||
| 161 | "Extend the font lock region according to HTML sub-mode needs. | ||
| 162 | |||
| 163 | This is used via `font-lock-extend-region-functions'. It ensures | ||
| 164 | that the font-lock region is extended to cover either whole | ||
| 165 | lines, or to the spot where the submode changes, whichever is | ||
| 166 | smallest." | ||
| 167 | (let ((orig-beg font-lock-beg) | ||
| 168 | (orig-end font-lock-end)) | ||
| 169 | ;; The logic here may look odd but it is needed to ensure that we | ||
| 170 | ;; do the right thing when trying to limit the search. | ||
| 171 | (save-excursion | ||
| 172 | (goto-char font-lock-beg) | ||
| 173 | ;; previous-single-property-change starts by looking at the | ||
| 174 | ;; previous character, but we're trying to extend a region to | ||
| 175 | ;; include just characters with the same submode as this | ||
| 176 | ;; character. | ||
| 177 | (unless (eobp) | ||
| 178 | (forward-char)) | ||
| 179 | (setq font-lock-beg (previous-single-property-change | ||
| 180 | (point) 'mhtml-submode nil | ||
| 181 | (line-beginning-position))) | ||
| 182 | (unless (eq (get-text-property font-lock-beg 'mhtml-submode) | ||
| 183 | (get-text-property orig-beg 'mhtml-submode)) | ||
| 184 | (cl-incf font-lock-beg)) | ||
| 185 | |||
| 186 | (goto-char font-lock-end) | ||
| 187 | (unless (bobp) | ||
| 188 | (backward-char)) | ||
| 189 | (setq font-lock-end (next-single-property-change | ||
| 190 | (point) 'mhtml-submode nil | ||
| 191 | (line-beginning-position 2))) | ||
| 192 | (unless (eq (get-text-property font-lock-end 'mhtml-submode) | ||
| 193 | (get-text-property orig-end 'mhtml-submode)) | ||
| 194 | (cl-decf font-lock-end))) | ||
| 195 | |||
| 196 | (or (/= font-lock-beg orig-beg) | ||
| 197 | (/= font-lock-end orig-end)))) | ||
| 198 | |||
| 199 | (defun mhtml--submode-fontify-one-region (submode beg end &optional loudly) | ||
| 200 | (if submode | ||
| 201 | (mhtml--with-locals submode | ||
| 202 | (save-restriction | ||
| 203 | (font-lock-fontify-region beg end loudly))) | ||
| 204 | (font-lock-set-defaults) | ||
| 205 | (font-lock-default-fontify-region beg end loudly))) | ||
| 206 | |||
| 207 | (defun mhtml--submode-fontify-region (beg end loudly) | ||
| 208 | (syntax-propertize end) | ||
| 209 | (let ((orig-beg beg) | ||
| 210 | (orig-end end) | ||
| 211 | (new-beg beg) | ||
| 212 | (new-end end)) | ||
| 213 | (while (< beg end) | ||
| 214 | (let ((submode (get-text-property beg 'mhtml-submode)) | ||
| 215 | (this-end (next-single-property-change beg 'mhtml-submode | ||
| 216 | nil end))) | ||
| 217 | (let ((extended (mhtml--submode-fontify-one-region submode beg | ||
| 218 | this-end loudly))) | ||
| 219 | ;; If the call extended the region, take note. We track the | ||
| 220 | ;; bounds we were passed and take the union of any extended | ||
| 221 | ;; bounds. | ||
| 222 | (when (and (consp extended) | ||
| 223 | (eq (car extended) 'jit-lock-bounds)) | ||
| 224 | (setq new-beg (min new-beg (cadr extended))) | ||
| 225 | ;; Make sure that the next region starts where the | ||
| 226 | ;; extension of this region ends. | ||
| 227 | (setq this-end (cddr extended)) | ||
| 228 | (setq new-end (max new-end this-end)))) | ||
| 229 | (setq beg this-end))) | ||
| 230 | (when (or (/= orig-beg new-beg) | ||
| 231 | (/= orig-end new-end)) | ||
| 232 | (cons 'jit-lock-bounds (cons new-beg new-end))))) | ||
| 233 | |||
| 234 | (defvar-local mhtml--last-submode nil | ||
| 235 | "Record the last visited submode, so the cursor-sensor function | ||
| 236 | can function properly.") | ||
| 237 | |||
| 238 | (defvar-local mhtml--stashed-crucial-variables nil | ||
| 239 | "Alist of stashed values of the crucial variables.") | ||
| 240 | |||
| 241 | (defun mhtml--stash-crucial-variables () | ||
| 242 | (setq mhtml--stashed-crucial-variables | ||
| 243 | (mapcar (lambda (sym) | ||
| 244 | (cons sym (buffer-local-value sym (current-buffer)))) | ||
| 245 | mhtml--crucial-variables))) | ||
| 246 | |||
| 247 | (defun mhtml--map-in-crucial-variables (alist) | ||
| 248 | (dolist (item alist) | ||
| 249 | (set (car item) (cdr item)))) | ||
| 250 | |||
| 251 | (defun mhtml--pre-command () | ||
| 252 | (let ((submode (get-text-property (point) 'mhtml-submode))) | ||
| 253 | (unless (eq submode mhtml--last-submode) | ||
| 254 | ;; If we're entering a submode, and the previous submode was | ||
| 255 | ;; nil, then stash the current values first. This lets the user | ||
| 256 | ;; at least modify some values directly. FIXME maybe always | ||
| 257 | ;; stash into the current mode? | ||
| 258 | (when (and submode (not mhtml--last-submode)) | ||
| 259 | (mhtml--stash-crucial-variables)) | ||
| 260 | (mhtml--map-in-crucial-variables | ||
| 261 | (if submode | ||
| 262 | (mhtml--submode-crucial-captured-locals submode) | ||
| 263 | mhtml--stashed-crucial-variables)) | ||
| 264 | (setq mhtml--last-submode submode)))) | ||
| 265 | |||
| 266 | (defun mhtml--syntax-propertize-submode (submode end) | ||
| 267 | (save-excursion | ||
| 268 | (when (search-forward (mhtml--submode-end-tag submode) end t) | ||
| 269 | (setq end (match-beginning 0)))) | ||
| 270 | (set-text-properties (point) end | ||
| 271 | (list 'mhtml-submode submode | ||
| 272 | 'syntax-table (mhtml--submode-syntax-table submode) | ||
| 273 | ;; We want local-map here so that we act | ||
| 274 | ;; more like the sub-mode and don't | ||
| 275 | ;; override minor mode maps. | ||
| 276 | 'local-map (mhtml--submode-keymap submode))) | ||
| 277 | (funcall (mhtml--submode-propertize submode) (point) end) | ||
| 278 | (goto-char end)) | ||
| 279 | |||
| 280 | (defun mhtml-syntax-propertize (start end) | ||
| 281 | ;; First remove our special settings from the affected text. They | ||
| 282 | ;; will be re-applied as needed. | ||
| 283 | (remove-list-of-text-properties start end | ||
| 284 | '(syntax-table local-map mhtml-submode)) | ||
| 285 | (goto-char start) | ||
| 286 | (when (and | ||
| 287 | ;; Don't search in a comment or string | ||
| 288 | (not (syntax-ppss-context (syntax-ppss))) | ||
| 289 | ;; Be sure to look back one character, because START won't | ||
| 290 | ;; yet have been propertized. | ||
| 291 | (not (bobp))) | ||
| 292 | (when-let ((submode (get-text-property (1- (point)) 'mhtml-submode))) | ||
| 293 | (mhtml--syntax-propertize-submode submode end))) | ||
| 294 | (funcall | ||
| 295 | (syntax-propertize-rules | ||
| 296 | ("<style.*?>" | ||
| 297 | (0 (ignore | ||
| 298 | (goto-char (match-end 0)) | ||
| 299 | ;; Don't apply in a comment. | ||
| 300 | (unless (syntax-ppss-context (syntax-ppss)) | ||
| 301 | (mhtml--syntax-propertize-submode mhtml--css-submode end))))) | ||
| 302 | ("<script.*?>" | ||
| 303 | (0 (ignore | ||
| 304 | (goto-char (match-end 0)) | ||
| 305 | ;; Don't apply in a comment. | ||
| 306 | (unless (syntax-ppss-context (syntax-ppss)) | ||
| 307 | (mhtml--syntax-propertize-submode mhtml--js-submode end))))) | ||
| 308 | sgml-syntax-propertize-rules) | ||
| 309 | ;; Make sure to handle the situation where | ||
| 310 | ;; mhtml--syntax-propertize-submode moved point. | ||
| 311 | (point) end)) | ||
| 312 | |||
| 313 | (defun mhtml-indent-line () | ||
| 314 | "Indent the current line as HTML, JS, or CSS, according to its context." | ||
| 315 | (interactive) | ||
| 316 | (let ((submode (save-excursion | ||
| 317 | (back-to-indentation) | ||
| 318 | (get-text-property (point) 'mhtml-submode)))) | ||
| 319 | (if submode | ||
| 320 | (save-restriction | ||
| 321 | (let* ((region-start | ||
| 322 | (or (previous-single-property-change (point) 'mhtml-submode) | ||
| 323 | (point))) | ||
| 324 | (base-indent (save-excursion | ||
| 325 | (goto-char region-start) | ||
| 326 | (sgml-calculate-indent)))) | ||
| 327 | (cond | ||
| 328 | ((eq mhtml-tag-relative-indent nil) | ||
| 329 | (setq base-indent (- base-indent sgml-basic-offset))) | ||
| 330 | ((eq mhtml-tag-relative-indent 'ignore) | ||
| 331 | (setq base-indent 0))) | ||
| 332 | (narrow-to-region region-start (point-max)) | ||
| 333 | (let ((prog-indentation-context (list base-indent | ||
| 334 | (cons (point-min) nil) | ||
| 335 | nil))) | ||
| 336 | (mhtml--with-locals submode | ||
| 337 | ;; indent-line-function was rebound by | ||
| 338 | ;; mhtml--with-locals. | ||
| 339 | (funcall indent-line-function))))) | ||
| 340 | ;; HTML. | ||
| 341 | (sgml-indent-line)))) | ||
| 342 | |||
| 343 | (defun mhtml--flyspell-check-word () | ||
| 344 | (let ((submode (get-text-property (point) 'mhtml-submode))) | ||
| 345 | (if submode | ||
| 346 | (flyspell-generic-progmode-verify) | ||
| 347 | t))) | ||
| 348 | |||
| 349 | ;;;###autoload | ||
| 350 | (define-derived-mode mhtml-mode html-mode | ||
| 351 | '((sgml-xml-mode "XHTML+" "HTML+") (:eval (mhtml--submode-lighter))) | ||
| 352 | "Major mode based on `html-mode', but works with embedded JS and CSS. | ||
| 353 | |||
| 354 | Code inside a <script> element is indented using the rules from | ||
| 355 | `js-mode'; and code inside a <style> element is indented using | ||
| 356 | the rules from `css-mode'." | ||
| 357 | (cursor-sensor-mode) | ||
| 358 | (setq-local indent-line-function #'mhtml-indent-line) | ||
| 359 | (setq-local parse-sexp-lookup-properties t) | ||
| 360 | (setq-local syntax-propertize-function #'mhtml-syntax-propertize) | ||
| 361 | (setq-local font-lock-fontify-region-function | ||
| 362 | #'mhtml--submode-fontify-region) | ||
| 363 | (setq-local font-lock-extend-region-functions | ||
| 364 | '(mhtml--extend-font-lock-region | ||
| 365 | font-lock-extend-region-multiline)) | ||
| 366 | |||
| 367 | ;; Attach this to both pre- and post- hooks just in case it ever | ||
| 368 | ;; changes a key binding that might be accessed from the menu bar. | ||
| 369 | (add-hook 'pre-command-hook #'mhtml--pre-command nil t) | ||
| 370 | (add-hook 'post-command-hook #'mhtml--pre-command nil t) | ||
| 371 | |||
| 372 | ;; Make any captured variables buffer-local. | ||
| 373 | (mhtml--mark-buffer-locals mhtml--css-submode) | ||
| 374 | (mhtml--mark-buffer-locals mhtml--js-submode) | ||
| 375 | |||
| 376 | (mhtml--mark-crucial-buffer-locals mhtml--css-submode) | ||
| 377 | (mhtml--mark-crucial-buffer-locals mhtml--js-submode) | ||
| 378 | (setq mhtml--crucial-variables (delete-dups mhtml--crucial-variables)) | ||
| 379 | |||
| 380 | ;: Hack | ||
| 381 | (js--update-quick-match-re) | ||
| 382 | |||
| 383 | ;; This is sort of a prog-mode as well as a text mode. | ||
| 384 | (run-hooks 'prog-mode-hook)) | ||
| 385 | |||
| 386 | (put 'mhtml-mode 'flyspell-mode-predicate #'mhtml--flyspell-check-word) | ||
| 387 | |||
| 388 | (provide 'mhtml-mode) | ||
| 389 | |||
| 390 | ;;; mhtml-mode.el ends here | ||
diff --git a/test/lisp/textmodes/mhtml-mode-tests.el b/test/lisp/textmodes/mhtml-mode-tests.el new file mode 100644 index 00000000000..d8eeb27643c --- /dev/null +++ b/test/lisp/textmodes/mhtml-mode-tests.el | |||
| @@ -0,0 +1,58 @@ | |||
| 1 | ;;; mhtml-mode-tests.el --- Tests for mhtml-mode | ||
| 2 | |||
| 3 | ;; Copyright (C) 2017 Free Software Foundation, Inc. | ||
| 4 | |||
| 5 | ;; Keywords: tests | ||
| 6 | |||
| 7 | ;; This file is part of GNU Emacs. | ||
| 8 | |||
| 9 | ;; GNU Emacs is free software: you can redistribute it and/or modify | ||
| 10 | ;; it under the terms of the GNU General Public License as published by | ||
| 11 | ;; the Free Software Foundation, either version 3 of the License, or | ||
| 12 | ;; (at your option) any later version. | ||
| 13 | |||
| 14 | ;; GNU Emacs is distributed in the hope that it will be useful, | ||
| 15 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 16 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 17 | ;; GNU General Public License for more details. | ||
| 18 | |||
| 19 | ;; You should have received a copy of the GNU General Public License | ||
| 20 | ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. | ||
| 21 | |||
| 22 | ;;; Commentary: | ||
| 23 | |||
| 24 | ;;; Code: | ||
| 25 | |||
| 26 | (require 'mhtml-mode) | ||
| 27 | (require 'ert) | ||
| 28 | |||
| 29 | (defun mhtml-test-syntax (before after what) | ||
| 30 | (with-temp-buffer | ||
| 31 | (mhtml-mode) | ||
| 32 | (insert before) | ||
| 33 | (save-excursion | ||
| 34 | (insert after)) | ||
| 35 | (font-lock-ensure) | ||
| 36 | (should (eq (syntax-ppss-context (syntax-ppss)) what)))) | ||
| 37 | |||
| 38 | (ert-deftest mthml-comment-js () | ||
| 39 | (mhtml-test-syntax "<html><script>\n/* " | ||
| 40 | " some text */<script></html>" | ||
| 41 | 'comment)) | ||
| 42 | |||
| 43 | (ert-deftest mthml-string-js () | ||
| 44 | (mhtml-test-syntax "<html><script>\n\" " | ||
| 45 | " some text \"<script></html>" | ||
| 46 | 'string)) | ||
| 47 | |||
| 48 | (ert-deftest mthml-comment-css () | ||
| 49 | (mhtml-test-syntax "<html><style>\n/* " | ||
| 50 | " some text */<style></html>" | ||
| 51 | 'comment)) | ||
| 52 | |||
| 53 | (ert-deftest mthml-string-css () | ||
| 54 | (mhtml-test-syntax "<html><style>\n\" " | ||
| 55 | " some text \"<style></html>" | ||
| 56 | 'string)) | ||
| 57 | |||
| 58 | ;;; mhtml-mode-tests.el ends here | ||
diff --git a/test/manual/indent/html-multi.html b/test/manual/indent/html-multi.html new file mode 100644 index 00000000000..a563c5eb244 --- /dev/null +++ b/test/manual/indent/html-multi.html | |||
| @@ -0,0 +1,30 @@ | |||
| 1 | <!DOCTYPE html> | ||
| 2 | <html> | ||
| 3 | <head> | ||
| 4 | <title>test</title> | ||
| 5 | <!-- | ||
| 6 | <script> | ||
| 7 | // No indent in a comment | ||
| 8 | if (true) { | ||
| 9 | do not change; | ||
| 10 | } | ||
| 11 | </script> | ||
| 12 | --> | ||
| 13 | |||
| 14 | <style type="text/css"> | ||
| 15 | h1 { | ||
| 16 | font-family: 'Spinnaker', sans-serif; | ||
| 17 | } | ||
| 18 | </style> | ||
| 19 | <script> | ||
| 20 | var a = 4 < 5; | ||
| 21 | function() { | ||
| 22 | if (x > 75) | ||
| 23 | return 25; | ||
| 24 | return "hello"; | ||
| 25 | } | ||
| 26 | </script> | ||
| 27 | </head> | ||
| 28 | <body> | ||
| 29 | </body> | ||
| 30 | </html> | ||