blob: ff1afd4018ef191b22ec9195f1adc1aa9b24b9cd [file] [log] [blame]
Brian Silverman70325d62015-09-20 17:00:43 -04001;;; tpl-mode.el -- a major mode for editing Google CTemplate files.
2;;; By Tony Gentilcore, July 2006
3;;;
4;;; TO USE:
5;;; 1) Copy this file somewhere you in emacs load-path. To see what
6;;; your load-path is, run inside emacs: C-h v load-path<RET>
7;;; 2) Add the following two lines to your .emacs file:
8;;; (setq auto-mode-alist (cons '("\\.tpl$" . tpl-mode) auto-mode-alist))
9;;; (autoload 'tpl-mode "tpl-mode" "Major mode for editing CTemplate files." t)
10;;; 3) Optionally (but recommended), add this third line as well:
11;;; (add-hook 'tpl-mode-hook '(lambda () (font-lock-mode 1)))
12;;; ---
13;;;
14;;; While the CTemplate language can be used for any types of text,
15;;; this mode is intended for using CTemplate to write HTML.
16;;;
17;;; The indentation still has minor bugs due to the fact that
18;;; templates do not require valid HTML.
19;;;
20;;; It would be nice to be able to highlight attributes of HTML tags,
21;;; however this is difficult due to the presence of CTemplate symbols
22;;; embedded within attributes.
23
24(eval-when-compile
25 (require 'font-lock))
26
27(defgroup tpl-mode nil
28 "Major mode for editing CTemplate files"
29 :group 'languages)
30
31(defvar tpl-mode-version "1.0"
32 "Version of `tpl-mode.el'.")
33
34(defvar tpl-mode-abbrev-table nil
35 "Abbrev table for use in tpl-mode buffers.")
36
37(define-abbrev-table 'tpl-mode-abbrev-table ())
38
39(defcustom tpl-mode-hook nil
40 "*Hook that runs upon entering tpl-mode."
41 :type 'hook
42 )
43
44(defvar tpl-mode-map nil
45 "Keymap for tpl-mode major mode")
46
47(if tpl-mode-map
48 nil
49 (setq tpl-mode-map (make-sparse-keymap))
50 )
51
52(define-key tpl-mode-map "\t" 'tpl-indent-command)
53(define-key tpl-mode-map "\C-m" 'newline-and-indent)
54
55
56(defvar tpl-mode-syntax-table nil
57 "Syntax table in use in tpl-mode buffers.")
58
59;; Syntax table.
60(if tpl-mode-syntax-table
61 nil
62 (setq tpl-mode-syntax-table (make-syntax-table text-mode-syntax-table))
63 (modify-syntax-entry ?< "(> " tpl-mode-syntax-table)
64 (modify-syntax-entry ?> ")< " tpl-mode-syntax-table)
65 (modify-syntax-entry ?\" ". " tpl-mode-syntax-table)
66 (modify-syntax-entry ?\\ ". " tpl-mode-syntax-table)
67 (modify-syntax-entry ?' "w " tpl-mode-syntax-table)
68 )
69
70(defvar tpl-basic-offset 2
71 "The basic indentation offset.")
72
73;; Constant regular expressions to identify template elements.
74(defconst tpl-mode-tpl-token "[a-zA-Z][a-zA-Z0-9_:=\-]*?")
75(defconst tpl-mode-section (concat "\\({{[#/]"
76 tpl-mode-tpl-token
77 "}}\\)"))
78(defconst tpl-mode-open-section (concat "\\({{#"
79 tpl-mode-tpl-token
80 "}}\\)"))
81(defconst tpl-mode-close-section (concat "{{/\\("
82 tpl-mode-tpl-token
83 "\\)}}"))
84(defconst tpl-mode-comment "\\({{![^}]+?}}\\)")
85(defconst tpl-mode-include (concat "\\({{>"
86 tpl-mode-tpl-token
87 "}}\\)"))
88(defconst tpl-mode-variable (concat "\\({{"
89 tpl-mode-tpl-token
90 "}}\\)"))
91(defconst tpl-mode-builtins
92 (concat
93 "\\({{\\<"
94 (regexp-opt
95 '("BI_NEWLINE" "BI_SPACE")
96 t)
97 "\\>}}\\)"))
98(defconst tpl-mode-close-section-at-start (concat "^[ \t]*?"
99 tpl-mode-close-section))
100
101;; Constant regular expressions to identify html tags.
102;; Taken from HTML 4.01 / XHTML 1.0 Reference found at:
103;; http://www.w3schools.com/tags/default.asp.
104(defconst tpl-mode-html-constant "\\(&#?[a-z0-9]\\{2,5\\};\\)")
105(defconst tpl-mode-pair-tag
106 (concat
107 "\\<"
108 (regexp-opt
109 '("a" "abbr" "acronym" "address" "applet" "area" "b" "bdo"
110 "big" "blockquote" "body" "button" "caption" "center" "cite"
111 "code" "col" "colgroup" "dd" "del" "dfn" "dif" "div" "dl"
112 "dt" "em" "fieldset" "font" "form" "frame" "frameset" "h1"
113 "h2" "h3" "h4" "h5" "h6" "head" "html" "i" "iframe" "ins"
114 "kbd" "label" "legend" "li" "link" "map" "menu" "noframes"
115 "noscript" "object" "ol" "optgroup" "option" "p" "pre" "q"
116 "s" "samp" "script" "select" "small" "span" "strike"
117 "strong" "style" "sub" "sup" "table" "tbody" "td" "textarea"
118 "tfoot" "th" "thead" "title" "tr" "tt" "u" "ul" "var")
119 t)
120 "\\>"))
121(defconst tpl-mode-standalone-tag
122 (concat
123 "\\<"
124 (regexp-opt
125 '("base" "br" "hr" "img" "input" "meta" "param")
126 t)
127 "\\>"))
128(defconst tpl-mode-open-tag (concat "<\\("
129 tpl-mode-pair-tag
130 "\\)"))
131(defconst tpl-mode-close-tag (concat "</\\("
132 tpl-mode-pair-tag
133 "\\)>"))
134(defconst tpl-mode-close-tag-at-start (concat "^[ \t]*?"
135 tpl-mode-close-tag))
136
137(defconst tpl-mode-blank-line "^[ \t]*?$")
138(defconst tpl-mode-dangling-open (concat "\\("
139 tpl-mode-open-section
140 "\\)\\|\\("
141 tpl-mode-open-tag
142 "\\)[^/]*$"))
143
144(defun tpl-indent-command ()
145 "Command for indenting text. Just calls tpl-indent."
146 (interactive)
147 (tpl-indent))
148
149;; Function to control indenting.
150(defun tpl-indent ()
151 "Indent current line"
152 ;; Set the point to beginning of line.
153 (beginning-of-line)
154 ;; If we are at the beginning of the file, indent to 0.
155 (if (bobp)
156 (indent-line-to 0)
157 (let ((tag-stack 1) (close-tag "") (cur-indent 0) (old-pnt (point-marker))
158 (close-at-start) (open-token) (dangling-open))
159 (progn
160 ;; Determine if this is a template line or an html line.
161 (if (looking-at "^[ \t]*?{{")
162 (setq close-at-start tpl-mode-close-section-at-start
163 open-token "{{#")
164 (setq close-at-start tpl-mode-close-tag-at-start
165 open-token "<")
166 )
167 ;; If there is a closing tag at the start of the line, search back
168 ;; for its opener and indent to that level.
169 (if (looking-at close-at-start)
170 (progn
171 (save-excursion
172 (setq close-tag (match-string 1))
173 ;; Keep searching for a match for the close tag until
174 ;; the tag-stack is 0.
175 (while (and (not (bobp))
176 (> tag-stack 0)
177 (re-search-backward (concat open-token
178 "\\(/?\\)"
179 close-tag) nil t))
180 (if (string-equal (match-string 1) "/")
181 ;; We found another close tag, so increment tag-stack.
182 (setq tag-stack (+ tag-stack 1))
183 ;; We found an open tag, so decrement tag-stack.
184 (setq tag-stack (- tag-stack 1))
185 )
186 (setq cur-indent (current-indentation))
187 )
188 )
189 (if (> tag-stack 0)
190 (save-excursion
191 (forward-line -1)
192 (setq cur-indent (current-indentation))
193 )
194 )
195 )
196 ;; This was not a closing tag, so we check if the previous line
197 ;; was an opening tag.
198 (save-excursion
199 ;; Keep moving back until we find a line that is not blank
200 (while (progn
201 (forward-line -1)
202 (and (not (bobp)) (looking-at tpl-mode-blank-line))
203 )
204 )
205 (setq cur-indent (current-indentation))
206 (if (re-search-forward tpl-mode-dangling-open old-pnt t)
207 (setq cur-indent (+ cur-indent tpl-basic-offset))
208 )
209 )
210 )
211 ;; Finally, we execute the actual indentation.
212 (if (> cur-indent 0)
213 (indent-line-to cur-indent)
214 (indent-line-to 0)
215 )
216 )
217 )
218 )
219 )
220
221;; controls highlighting
222(defconst tpl-mode-font-lock-keywords
223 (list
224 (list tpl-mode-section
225 '(1 font-lock-keyword-face))
226 (list tpl-mode-comment
227 '(1 font-lock-comment-face))
228 (list tpl-mode-include
229 '(1 font-lock-builtin-face))
230 (list tpl-mode-builtins
231 '(1 font-lock-variable-name-face))
232 (list tpl-mode-variable
233 '(1 font-lock-reference-face))
234 (list (concat "</?\\(" tpl-mode-pair-tag "\\)")
235 '(1 font-lock-function-name-face))
236 (list (concat "<\\(" tpl-mode-standalone-tag "\\)")
237 '(1 font-lock-function-name-face))
238 (list tpl-mode-html-constant
239 '(1 font-lock-variable-name-face))
240 ))
241
242(put 'tpl-mode 'font-lock-defaults '(tpl-font-lock-keywords nil t))
243
244(defun tpl-mode ()
245 "Major mode for editing CTemplate file."
246 (interactive)
247 (kill-all-local-variables)
248 (use-local-map tpl-mode-map)
249 (setq major-mode 'tpl-mode)
250 (setq mode-name "tpl-mode")
251 (setq local-abbrev-table tpl-mode-abbrev-table)
252 (setq indent-tabs-mode nil)
253 (set-syntax-table tpl-mode-syntax-table)
254 ; show trailing whitespace, but only when the user can fix it
255 (setq show-trailing-whitespace (not buffer-read-only))
256 (make-local-variable 'indent-line-function)
257 (setq indent-line-function 'tpl-indent)
258 (setq font-lock-defaults '(tpl-mode-font-lock-keywords))
259 (run-hooks 'tpl-mode-hook)
260)
261
262(provide 'tpl-mode)