/usr/share/emacs/site-lisp/mmm-mode/mmm-erb.el is in mmm-mode 0.5.1-2.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 | ;;; mmm-erb.el --- ERB templates editing support
;; Copyright (C) 2012, 2013 by Dmitry Gutov
;; Author: Dmitry Gutov <dgutov@yandex.ru>
;;{{{ GPL
;; This file 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 2, or (at your option)
;; any later version.
;; This file 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; see the file COPYING. If not, write to
;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.
;;}}}
;;; Commentary:
;; This file contains definitions of ERB and EJS submode classes, and well as
;; support functions for proper indentation.
;; Usage:
;; (require 'mmm-auto)
;; (setq mmm-global-mode 'auto)
;; (mmm-add-mode-ext-class 'html-erb-mode "\\.html\\.erb\\'" 'erb)
;; (mmm-add-mode-ext-class 'html-erb-mode "\\.jst\\.ejs\\'" 'ejs)
;; (mmm-add-mode-ext-class 'html-erb-mode nil 'html-js)
;; (mmm-add-mode-ext-class 'html-erb-mode nil 'html-css)
;; (add-to-list 'auto-mode-alist '("\\.html\\.erb\\'" . html-erb-mode))
;; (add-to-list 'auto-mode-alist '("\\.jst\\.ejs\\'" . html-erb-mode))
;; Optional settings:
;; (setq mmm-submode-decoration-level 2
;; mmm-parse-when-idle t)
;; nXML as primary mode (supports only JS and CSS subregions):
;; (mmm-add-mode-ext-class 'nxml-web-mode nil 'html-js)
;; (mmm-add-mode-ext-class 'nxml-web-mode nil 'html-css)
;; (add-to-list 'auto-mode-alist '("\\.xhtml\\'" . nxml-web-mode))
;;; Code:
(require 'sgml-mode)
(eval-when-compile (require 'cl))
(require 'mmm-vars)
(require 'mmm-region)
(mmm-add-classes
'((erb :submode ruby-mode :front "<%[#=]?" :back "-?%>"
:match-face (("<%#" . mmm-comment-submode-face)
("<%=" . mmm-output-submode-face)
("<%" . mmm-code-submode-face))
:insert ((?% erb-code nil @ "<%" @ " " _ " " @ "%>" @)
(?# erb-comment nil @ "<%#" @ " " _ " " @ "%>" @)
(?= erb-expression nil @ "<%=" @ " " _ " " @ "%>" @))
:creation-hook mmm-erb-mark-as-special)
(ejs :submode js-mode :front "<%[#=]?" :back "-?%>"
:match-face (("<%#" . mmm-comment-submode-face)
("<%=" . mmm-output-submode-face)
("<%" . mmm-code-submode-face))
:insert ((?% ejs-code nil @ "<%" @ " " _ " " @ "%>" @)
(?# ejs-comment nil @ "<%#" @ " " _ " " @ "%>" @)
(?= ejs-expression nil @ "<%=" @ " " _ " " @ "%>" @))
:creation-hook mmm-erb-mark-as-special)))
;;;###autoload
(define-derived-mode html-erb-mode html-mode "ERB-HTML"
(setq sgml-unclosed-tags nil) ; Simplifies indentation logic.
(set (make-local-variable 'mmm-indent-line-function) 'mmm-erb-indent-line))
(defun mmm-erb-mark-as-special ()
"Hook function to run in ERB and EJS tag regions."
(overlay-put mmm-current-overlay 'mmm-special-tag t))
(defun mmm-erb-indent-line ()
"Indent the current line intelligently."
(interactive)
(let ((offset (- (current-column) (current-indentation))))
(back-to-indentation)
(mmm-update-submode-region)
(if (and mmm-current-overlay mmm-current-submode
(< (overlay-start mmm-current-overlay) (point-at-bol)))
;; Region starts before the current line (and contains indentation).
;; If it starts on the current line, then either first part of the line
;; is in primary mode, or we're on the first line of a script or style
;; tag contents. In the latter case, better to also indent it according
;; to the primary mode (as text): `js-indent-line' ignores narrowing,
;; gets confused by the angle bracket on the previous line and thus
;; breaks our "top level" heuristic.
(mmm-erb-indent-line-submode)
(mmm-erb-indent-line-primary))
(when (> offset 0) (forward-char offset))))
(defun mmm-erb-indent-line-submode ()
"Indent line within a submode."
(let (added-whitespace)
(if (<= (overlay-end mmm-current-overlay)
(save-excursion (back-to-indentation) (point)))
;; We're at a closing tag.
(mmm-erb-indent-to-region-start)
(save-restriction
(save-excursion
(goto-char (overlay-start mmm-current-overlay))
(when (not (looking-at "^\\|\\s-*$"))
;; Submode region has text on the same line as the opening tag,
;; pad it with whitespace to make the following lines line up.
(setq added-whitespace (current-column))
(insert-char ?\s added-whitespace)))
(narrow-to-region (overlay-start mmm-current-overlay)
(overlay-end mmm-current-overlay))
(funcall (mmm-erb-orig-indent-function mmm-current-submode))
(when added-whitespace
;; Remove the padding.
(save-excursion
(goto-char (overlay-start mmm-current-overlay))
(delete-char added-whitespace))))
;; If submode indent function moved us to bol,
;; we're on the top level, indent according to the primary mode.
(when (zerop (current-indentation))
(mmm-erb-indent-to-region-start
(mmm-erb-indent-offset mmm-primary-mode))))))
(defun mmm-erb-indent-to-region-start (&optional additional-offset)
"Indent line to match start of region, possibly adding ADDITIONAL-OFFSET."
(let ((indent (current-indentation)))
(indent-line-to
(save-excursion
(goto-char (1- (overlay-start mmm-current-overlay)))
(+ (current-indentation)
(or additional-offset 0))))))
(defun mmm-erb-indent-line-primary ()
"Indent line in primary mode."
(let* ((here (point))
;; Go before previous line's tag.
(start (progn (forward-line -1)
(back-to-indentation)
(let ((lcon (sgml-lexical-context)))
(when (eq (car lcon) 'tag)
;; Tag spreads several lines.
(goto-char (cdr lcon))
(back-to-indentation)))
(point)))
(regions (mmm-regions-in start here))
(n 0))
;; Collect indent modifier depending on type of tags.
(loop for region in regions
for type = (mmm-erb-scan-region region)
when type do
(if (eq type 'close)
(when (plusp n) (decf n))
(incf n (if (eq type 'close) 0 1))))
(let ((eol (progn (goto-char here) (end-of-line 1) (point))))
;; Look for "else" and "end" instructions to adjust modifier.
;; If a block start instruction comes first, abort.
(loop for region in (mmm-regions-in here eol)
for type = (mmm-erb-scan-region region)
until (eq type 'open)
when (memq type '(middle close)) do (decf n)))
(goto-char here)
(funcall (mmm-erb-orig-indent-function mmm-primary-mode))
(let* ((indent (current-indentation))
(indent-step (mmm-erb-indent-offset mmm-primary-mode)))
(indent-line-to (+ indent (if n (* indent-step n) 0))))))
(defun mmm-erb-scan-region (region)
(when region ; Can be nil if a line is empty, for example.
(destructuring-bind (submode beg end ovl) region
(let ((scan-fn (plist-get '(ruby-mode mmm-erb-scan-erb
js-mode mmm-erb-scan-ejs)
submode)))
(and scan-fn
(overlay-get ovl 'mmm-special-tag)
(save-excursion
(goto-char beg)
(skip-syntax-forward "-")
(funcall scan-fn end)))))))
(defconst mmm-erb-ruby-close-re "\\<end\\>\\|}"
"Regexp to match the end of a Ruby block.")
(defun mmm-erb-scan-erb (limit)
(cond ((looking-at "\\(?:if\\|unless\\|for\\|while\\)\\b") 'open)
((looking-at "\\(?:else\\|elsif\\)\\b") 'middle)
((looking-at mmm-erb-ruby-close-re) 'close)
((and (re-search-forward (concat "\\(?: +do +\\| *{ *\\)"
"\\(?:|[A-Za-z0-9_, ]*|\\)? *")
limit t)
(not (re-search-forward mmm-erb-ruby-close-re limit t)))
'open)))
(defun mmm-erb-scan-ejs (limit)
(cond ((looking-at "\\(?:if\\|for\\|while\\)\\b") 'open)
((looking-at "} *else\\b") 'middle)
((looking-at "}") 'close)
((re-search-forward " *{ *" limit t) 'open)))
(defun mmm-erb-orig-indent-function (mode)
(get mode 'mmm-indent-line-function))
(defvar mmm-erb-offset-var-alist
'((html-erb-mode . sgml-basic-offset)
(nxml-web-mode . nxml-child-indent)))
(defun mmm-erb-indent-offset (mode)
(let ((name (cdr (assoc mode mmm-erb-offset-var-alist))))
(when name (symbol-value name))))
;;;###autoload
(define-derived-mode nxml-web-mode nxml-mode "nXML-Web"
(set (make-local-variable 'mmm-indent-line-function) 'mmm-erb-indent-line))
(provide 'mmm-erb)
;;; mmm-erb.el ends here
|