(eval-when-compile
(require 'cl)
(require 'regexp-opt)
(require 'font-lock))
(defvar template-minor-mode nil
"Template Toolkit minor mode.")
(defvar template-mode-map nil)
(unless template-mode-map
(setq template-mode-map (make-sparse-keymap))
(define-key template-mode-map "\C-m" 'newline-and-indent)
(define-key template-mode-map "\C-l" 'recenter)
(define-key template-mode-map "[" 'template-mode-electric-brace)
(define-key template-mode-map "]" 'template-mode-electric-brace))
(defun template-mode-electric-brace (arg)
"Insert the key typed and maybe correct line's indentation."
(interactive "P")
(self-insert-command (prefix-numeric-value arg))
(if (not (looking-at "[ \t]*\\\\?$"))
(indent-according-to-mode)))
(defun template-mode-flatten (lst)
"In LST undoes any nesting of sub-lists, causing all elements to be on the
top-level. All removes all `nil' elements. See \"Writing GNU Emcas
extensions\" by B. Glickstein, p. 86."
(if (null lst) nil
(if (listp (car lst))
(append (template-mode-flatten (car lst))
(template-mode-flatten (cdr lst)))
(cons (car lst)
(template-mode-flatten (cdr lst))))))
(defun template-mode-join (sep &rest args)
"Like Perl's `join'."
(mapconcat '(lambda(x)x) (template-mode-flatten args) sep))
(defun template-mode-regexp (strings &optional func)
"Group Emacs regular expressions in the list STRINGS.
FUNC controls how strings are transformed and grouped. Legal arguments are
nil (don't group), t (group), `shy' (shy group), `words' (match the empty
string before/after STRINGS), or `shy-words'."
(if (listp strings)
(if (null strings) ""
(save-match-data
(let ((shy (or (eq func 'shy) (eq func 'shy-words)))
(words (or (eq func 'words) (eq func 'shy-words)))
(sorted-strings
(template-mode-flatten (delete-dups (sort (copy-sequence strings) 'string-lessp)))))
(if (= (length sorted-strings) 0) ""
(let ((open (if func (if shy "\\(?:" "\\(") ""))
(close (if func "\\)" ""))
(re (template-mode-join "\\|" sorted-strings)))
(concat open (if words (concat "\\<\\(?:" re "\\)\\>") re) close)))
)
)
)
(template-mode-regexp (list strings) func)))
(defun template-mode-tt2-regexp (strings &optional all)
"Build regular expression matching a Template Toolkit tag.
If ALL is nil `match-string' 1 will return the inner part."
(if (listp strings)
(template-mode-regexp
(list (concat (template-mode-regexp template-mode-open-re all)
(template-mode-regexp strings t)
(template-mode-regexp template-mode-close-re all))))
(template-mode-tt2-regexp (list strings) all)))
(defconst template-mode-open-re (template-mode-regexp (list "\\[%[-+]?\\s-*" "\\[\\[[-+]?\\s-*") 'shy))
(defconst template-mode-close-re (template-mode-regexp (list "\\s-*%\\]" "\\s-*\\]\\]") 'shy))
(defconst template-mode-keyword-list
'("block" "call" "catch" "case" "clear" "default" "end" "elsif" "else"
"for" "foreach" "filter" "final" "get"
"if" "insert" "include"
"process" "last"
"macro" "meta" "perl"
"unless" "use" "wrapper" "while" "rawperl"
"return" "stop" "switch" "set" "tags" "try" "throw")
"List of Template Toolkit keywords.")
(defvar template-mode-face-alist
'(("default" . default)
("bold" . bold)
("highlight" . highlight)
("emphasize" . italic)
("underline" . underline)
("modeline" . modeline)
("bu" bold underline)
("beu" bold-italic underline)
("eu" italic underline)
("small" . default)
("strong" . bold)))
(defvar template-mode-font-lock-1
(list
(list (concat (template-mode-regexp "\\$" t)
(template-mode-regexp ".*?" t)
(template-mode-regexp "\\$" t))
'(1 font-lock-builtin-face t)
'(2 font-lock-comment-face t)
'(3 font-lock-builtin-face t))
(list (template-mode-tt2-regexp ".+?" t)
'(1 font-lock-builtin-face t)
'(2 font-lock-constant-face t)
'(3 font-lock-builtin-face t))
(list (template-mode-tt2-regexp "#.*?")
'(1 font-lock-comment-face t))
(list (template-mode-tt2-regexp (list template-mode-keyword-list))
'(1 font-lock-keyword-face t))
)
"Expressions to font-lock in `template-minor-mode'.")
(defconst template-mode-font-lock-2
(append
template-mode-font-lock-1
'((eval . (cons
(template-mode-tt2-regexp ".+?")
'(1 (cdr (assoc "bold" template-mode-face-alist)) prepend))))
)
"Extend `template-mode-font-lock-1' so that all tags are rendered bold, and
variables additionally underlined.")
(defun template-mode-turn-on-font-lock ()
(turn-on-font-lock)
(font-lock-add-keywords nil template-mode-font-lock-2)
(font-lock-fontify-buffer))
(defun turn-on-template-mode ()
"Unconditionally turn on Template-Toolkit minor mode."
(interactive) (template-minor-mode 1))
(defun turn-off-template-mode ()
"Unconditionally turn off Template-Toolkit minor mode."
(interactive) (template-minor-mode 0))
(define-minor-mode template-minor-mode
"Toggle Template-Toolkit minor-mode.
Basically `template-minor-mode' adds fontifying for tagged expressions the
Template Toolkit would replace."
nil " Template" template-mode-map
(progn
(template-mode-turn-on-font-lock)
(run-hooks 'template-mode-hook)))
(defun outline-template-mode ()
"Major mode for editing text files to be expanded by the Perl Template Toolkit.
Actually a hybrid mode combining `outline-mode' and `template-minor-mode'."
(interactive)
(outline-mode)
(template-minor-mode))
(defun template-mode ()
"Major mode for editing text files to be expanded by the Perl Template Toolkit.
This mode fontifies embedded tags; files are otherwise in `fundamental-mode' or
`text-mode'. See also `outline-template-mode'."
(kill-all-local-variables)
(setq major-mode 'template-mode)
(setq mode-name "Template")
(template-mode-turn-on-font-lock)
(run-hooks 'template-mode-hook))
(provide 'template-minor-mode)
(provide 'template-mode)
(provide 'outline-template-mode)