+++ /dev/null
-;;; mustache-autoloads.el --- automatically extracted autoloads
-;;
-;;; Code:
-
-\f
-;;;### (autoloads nil nil ("mustache-lex.el" "mustache-parse.el"
-;;;;;; "mustache-pkg.el" "mustache-render.el" "mustache.el") (21490
-;;;;;; 25514 540076 0))
-
-;;;***
-\f
-(provide 'mustache-autoloads)
-;; Local Variables:
-;; version-control: never
-;; no-byte-compile: t
-;; no-update-autoloads: t
-;; coding: utf-8
-;; End:
-;;; mustache-autoloads.el ends here
+++ /dev/null
-(eval-when-compile (require 'cl)) ;; first, second, destructuring-bind, loop
-(require 's)
-
-(defun mst--lex (template)
- "Iterate through TEMPLATE, splitting {{ tags }} and bare strings.
-We return a list of lists: ((:text \"foo\") (:tag \"variable-name\"))"
- ;; convert {{{foo}}} to {{& foo}}
- (setq template (replace-regexp-in-string "{{{\\(.*?\\)}}}" "{{& \\1}}" template))
-
- (let ((open-delimeter "{{")
- (close-delimeter "}}")
- (lexemes nil))
- (while (not (s-equals? template ""))
- (let* ((open-index (s-index-of open-delimeter template))
- (close-index (s-index-of close-delimeter template)))
- ;; todo: check open-index < close-index
- ;; todo: error if we have an open and no close
- (if (and open-index close-index)
- ;; we have a tag
- (let ((between-delimeters
- (substring template (+ open-index (length open-delimeter)) close-index))
- (continue-from-index (+ close-index (length close-delimeter))))
- ;; save the string before the tag
- (when (> open-index 0)
- (push (list :text (substring template 0 open-index)) lexemes))
-
- ;; if this is a tag that changes delimeters e.g. {{=<< >>=}}
- ;; then set the new open/close delimeter string
- (if (s-matches-p "^=.+ .+=$" between-delimeters)
- (let* (;; strip leading/trailing =
- (delimeter-spec (substring between-delimeters 1 -1))
- (spec-parts (s-split " " delimeter-spec)))
- (setq open-delimeter (first spec-parts))
- (setq close-delimeter (second spec-parts)))
-
- ;; otherwise it's a normal tag, so save it
- (push (list :tag (s-trim between-delimeters)) lexemes))
-
- ;; iterate on the remaining template
- (setq template
- (substring template continue-from-index)))
- ;; else only plain text left
- (progn
- (push (list :text template) lexemes)
- (setq template "")))))
- (nreverse lexemes)))
-
-(defun mst--clean-whitespace (lexemes)
- "Given a list of LEXEMES, remove whitespace around sections and
-comments if they're on their own on a line. Modifies the original
-list."
- ;; iterate over all lexemes:
- (loop for i from 0 to (- (length lexemes) 3)
- collect
- ;; find sections that have plain text before and after
- (let ((first (elt lexemes i))
- (second (elt lexemes (+ i 1)))
- (third (elt lexemes (+ i 2))))
- (when (and (mst--text-p first)
- (or
- (mst--section-tag-p second)
- (mst--comment-tag-p second))
- (mst--text-p third)
- ;; check the section is on its own line
- (string-match-p "\n *$" (mst--tag-text first))
- (string-match-p "^\n" (mst--tag-text third)))
- ;; then we cleanup whitespace
- (setf (elt lexemes i) (mst--no-trailing-newline first)))))
- lexemes)
-
-(defalias 'mst--tag-text 'second
- "Returns the text context of a tag.")
-
-(defun mst--no-trailing-newline (lexeme)
- "Replace \"\n\" or \"\n \" at the end of a plain text lexeme."
- (list
- :text
- (replace-regexp-in-string "\n *$" "" (mst--tag-text lexeme))))
-
-(defun mst--tag-p (lexeme)
- "Is LEXEME a tag?"
- (equal (car lexeme) :tag))
-
-(defun mst--section-tag-p (lexeme)
- "Is LEXEME a section tag?"
- (or
- (mst--open-section-tag-p lexeme)
- (mst--inverted-section-tag-p lexeme)
- (mst--close-section-tag-p lexeme)))
-
-(defun mst--open-section-tag-p (lexeme)
- "Is LEXEME an open section tag?
-See also `mst--inverted-section-tag-p'."
- (and (mst--tag-p lexeme)
- (s-starts-with-p "#" (second lexeme))))
-
-(defun mst--inverted-section-tag-p (lexeme)
- "Is LEXEME an inverted section tag?"
- (and (mst--tag-p lexeme)
- (s-starts-with-p "^" (second lexeme))))
-
-(defun mst--close-section-tag-p (lexeme)
- "Is LEXEME a close section tag?"
- (and (mst--tag-p lexeme)
- (s-starts-with-p "/" (second lexeme))))
-
-(defun mst--comment-tag-p (lexeme)
- "Is LEXEME a comment tag?"
- (and (mst--tag-p lexeme)
- (s-starts-with-p "!" (second lexeme))))
-
-(defun mst--unescaped-tag-p (lexeme)
- "Is LEXEME an unescaped variable tag?
-Note that the lexer converts {{{foo}}} to {{& foo}}."
- (and (mst--tag-p lexeme)
- (s-starts-with-p "&" (second lexeme))))
-
-(defun mst--partial-tag-p (lexeme)
- "Is LEXEME a partial tag?"
- (and (mst--tag-p lexeme)
- (s-starts-with-p ">" (second lexeme))))
-
-(defun mst--section-p (lexeme)
- "Is LEXEME a nested section?"
- (not (atom (car lexeme))))
-
-(defun mst--text-p (lexeme)
- "Is LEXEME plain text?"
- (equal (car lexeme) :text))
-
-;; fixme: assumes the delimeters haven't changed
-;; fixme: mst--lex doens't preserve whitespace
-(defun mst--unlex (lexemes)
- "Given a lexed (and optionally parsed) list of lexemes,
-return the original input string."
- (if lexemes
- (let ((lexeme (first lexemes))
- (rest (cdr lexemes)))
- (cond
- ;; recurse on this section, then the rest
- ((mst--section-p lexeme)
- (concat (mst--unlex lexeme) (mst--unlex rest)))
- ((mst--tag-p lexeme)
- ;; restore the delimeters, then unlex the rest
- (let ((tag-name (second lexeme)))
- (concat "{{" tag-name "}}" (mst--unlex rest))))
- ;; otherwise, it's just raw text
- (t
- (let ((text (second lexeme)))
- (concat text (mst--unlex rest))))))
- ""))
+++ /dev/null
-(require 's)
-(eval-when-compile (require 'cl)) ;; loop, return
-
-(defvar mst--remaining-lexemes nil
- "Since `mst--parse-inner' recursively calls itself, we need a shared value to mutate.")
-
-(defun mst--parse (lexemes)
- "Given a list LEXEMES, return a list of lexemes nested according to #tags or ^tags."
- (setq mst--remaining-lexemes lexemes)
- (mst--parse-inner))
-
-(defun mst--parse-inner (&optional section-name)
- "Parse `mst--remaining-lexemes', and return a list of lexemes nested according to #tags or ^tags."
- (let (parsed-lexemes
- lexeme)
- (loop while mst--remaining-lexemes do
- (setq lexeme (pop mst--remaining-lexemes))
- (cond
- ((mst--open-section-p lexeme)
- ;; recurse on this nested section
- (push (cons lexeme (mst--parse-inner (mst--section-name lexeme))) parsed-lexemes))
- ((mst--close-section-p lexeme)
- ;; this is the last tag in this section
- (unless (equal section-name (mst--section-name lexeme))
- (error "Mismatched brackets: You closed a section with %s, but it wasn't open" section-name))
- (push lexeme parsed-lexemes)
- (return))
- (t
- ;; this is just a tag in the current section
- (push lexeme parsed-lexemes))))
-
- ;; ensure we aren't inside an unclosed section
- (when (and section-name (not (mst--close-section-p lexeme)))
- (error "Unclosed section: You haven't closed %s" section-name))
-
- (nreverse parsed-lexemes)))
-
-(defun mst--open-section-p (lexeme)
- "Is LEXEME a #tag or ^tag ?"
- (destructuring-bind (type value) lexeme
- (and (equal type :tag)
- (or
- (s-starts-with-p "#" value)
- (s-starts-with-p "^" value)))))
-
-(defun mst--close-section-p (lexeme)
- "Is LEXEME a /tag ?"
- (destructuring-bind (type value) lexeme
- (and (equal type :tag)
- (s-starts-with-p "/" value))))
-
-(defun mst--section-name (lexeme)
- "Get the name of the section from LEXEME, a two part list returned by `mst--lex'.
-The leading character (the #, ^ or /) is stripped."
- (s-chop-prefixes '("#" "^" "/") (cadr lexeme)))
+++ /dev/null
-(define-package "mustache" "20131117.1407" "a mustache templating library in emacs lisp"
- '((ht "0.9")
- (s "1.3.0")
- (dash "1.2.0")))
+++ /dev/null
-(require 'ht)
-(require 's)
-(require 'dash)
-
-(eval-when-compile '(require 'cl)) ;; return, dolist
-
-(load "mustache-lex.el")
-(load "mustache-parse.el")
-
-(defun mst--render (template context)
- "Render a mustache TEMPLATE with hash table CONTEXT."
- (-> template
- mst--lex
- mst--clean-whitespace
- mst--parse
- (mst--render-section-list context)))
-
-(defun mst--mapconcat (function sequence)
- "Apply FUNCTION to every element in SEQUENCE, and concat the results as strings."
- (mapconcat function sequence ""))
-
-(defmacro mst--amapconcat (form sequence)
- "Anaphoric version of `mst--mapconcat'."
- `(mst--mapconcat (lambda (it) ,form) ,sequence))
-
-;; todo: set flag to set tolerance of missing templates
-(defun mst--get-partial (name)
- "Get the first partial whose file name is NAME.mustache, or nil otherwise.
-Partials are searched for in `mustache-partial-paths'."
- (unless (listp mustache-partial-paths)
- (error "`mustache-partial-paths' must be a list of paths"))
- (let ((partial-name (format "%s.mustache" name)))
- (dolist (path mustache-partial-paths)
- (-when-let*
- ((partials (directory-files path nil "\\.mustache$"))
- (matching-partial (--first
- (string-match-p (regexp-quote partial-name) it)
- partials)))
- (return
- (with-temp-buffer
- (insert-file-contents-literally (expand-file-name matching-partial path))
- (buffer-substring-no-properties (point-min) (point-max))))))))
-
-(defun mst--render-section-list (sections context)
- "Render a parsed list SECTIONS in CONTEXT."
- (mst--amapconcat (mst--render-section it context) sections))
-
-(defun mst--context-get (context variable-name &optional default)
- "Lookup VARIABLE-NAME in CONTEXT, returning DEFAULT if not present."
- (when (eq mustache-key-type 'keyword)
- (setq variable-name (intern (concat ":" variable-name))))
- (ht-get context variable-name default))
-
-(defun mst--render-tag (parsed-tag context)
- "Given PARSED-TAG, render it in hash table CONTEXT."
- (let ((inner-text (mst--tag-text parsed-tag)))
- (cond
- ((mst--comment-tag-p parsed-tag)
- "")
- ((mst--unescaped-tag-p parsed-tag)
- (let ((variable-value (mst--context-get context (s-trim (substring inner-text 1)) "")))
- (when (numberp variable-value)
- (setq variable-value (number-to-string variable-value)))
- variable-value))
- ((mst--partial-tag-p parsed-tag)
- (let ((partial (mst--get-partial (s-trim (substring inner-text 1)))))
- (if partial
- (mst--render partial context)
- "")))
- (t ;; normal variable
- (let ((variable-value (mst--context-get context inner-text "")))
- (when (numberp variable-value)
- (setq variable-value (number-to-string variable-value)))
- (mst--escape-html variable-value))))))
-
-(defun mst--context-add (table from-table)
- "Return a copy of TABLE where all the key-value pairs in FROM-TABLE have been set."
- (let ((new-table (ht-copy table)))
- (ht-update new-table from-table)
- new-table))
-
-(defun mst--listp (object)
- "Return t if OBJECT is a list.
-Unlike `listp', does not return t if OBJECT is a function."
- (and (not (functionp object)) (listp object)))
-
-(defun mst--section-name (section-tag)
- "Get the name of this SECCTION-TAG.
-E.g. from {{#foo}} to \"foo\"."
- (-> section-tag ;; e.g (:tag "#foo")
- mst--tag-text ;; to "#foo"
- (substring 1) ;; to "foo"
- s-trim))
-
-(defun mst--render-section (parsed-lexeme context)
- "Given PARSED-LEXEME -- a lexed tag, plain text, or a nested list,
-render it in CONTEXT."
- (cond ((mst--section-p parsed-lexeme)
- ;; nested section
- (let* ((section-tag (first parsed-lexeme))
- (section-name (mst--section-name section-tag))
- (context-value (mst--context-get context section-name))
- ;; strip section open and close
- (section-contents (-slice parsed-lexeme 1 -1)))
- (cond
- ((mst--open-section-tag-p section-tag)
- (cond
- ;; if the context is a list of hash tables, render repeatedly
- ((or (mst--listp context-value) (vectorp context-value))
- (mst--amapconcat (mst--render-section-list section-contents (mst--context-add context it)) context-value))
- ;; if the context is a hash table, render in that context
- ((hash-table-p context-value)
- (mst--render-section-list section-contents (mst--context-add context context-value)))
- ;; if the context is a function, call it
- ((functionp context-value)
- (funcall context-value (mst--unlex section-contents) context))
- ;; if it's a truthy value, render in the current context
- (context-value
- (mst--render-section-list section-contents context))
- ;; otherwise, don't render anything
- (t "")))
- ;; render ^tags
- ((mst--inverted-section-tag-p section-tag)
- (if context-value
- ""
- (mst--render-section-list section-contents context))))))
- ((mst--tag-p parsed-lexeme)
- (mst--render-tag parsed-lexeme context))
- ;; plain text
- (t
- (second parsed-lexeme))))
-
-(defun mst--escape-html (string)
- "Escape HTML in STRING."
- (->> string
- (s-replace "&" "&")
- (s-replace "<" "<")
- (s-replace ">" ">")
- (s-replace "'" "'")
- (s-replace "\"" """)))
+++ /dev/null
-;;; mustache.el --- a mustache templating library in emacs lisp
-
-;; Copyright (C) 2013 Wilfred Hughes
-
-;; Author: Wilfred Hughes <meNOSPAMwilfred.me.uk>
-;; Version: 0.23
-;; Keywords: mustache, template
-;; Package-Requires: ((ht "0.9") (s "1.3.0") (dash "1.2.0"))
-
-;; This program 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 3 of the License, or
-;; (at your option) any later version.
-
-;; This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
-
-;;; Commentary:
-
-;; See documentation at https://github.com/Wilfred/mustache.el
-
-;; Note on terminology: We treat mustache templates as a sequence of
-;; strings (plain text), and tags (anything wrapped in delimeters:
-;; {{foo}}). A section is a special tag that requires closing
-;; (e.g. {{#foo}}{{/foo}}).
-
-;; We treat mustache templates as if they conform to a rough grammar:
-
-;; TEMPLATE = plaintext | TAG | SECTION | TEMPLATE TEMPLATE
-;; SECTION = OPEN-TAG TEMPLATE CLOSE-TAG
-;; TAG = "{{" text "}}"
-
-;; Public functions are of the form `mustache-FOO`, private
-;; functions/variables are of the form `mst--FOO`.
-
-;;; Code:
-
-(load "mustache-render.el")
-
-;; todo: add flag to set tolerance of missing variables
-(defun mustache-render (template context)
- "Render a mustache TEMPLATE with hash table CONTEXT."
- (mst--render template context))
-
-(defvar mustache-partial-paths nil
- "A list of paths to be searched for mustache partial templates (files ending .mustache).")
-
-(defvar mustache-key-type 'string
- "What type of key we expect in contexts.
-Can take the value 'string or 'keyword.
-
-For 'string we expect contexts of the form:
-#s\(hash-table data \(\"name\" \"J. Random user\"\)\)
-
-for 'keyword we expect contexts of the form:
-#s\(hash-table data \(:name \"J. Random user\"\)\)
-")
-
-(provide 'mustache)
-;;; mustache.el ends here
--- /dev/null
+;;; mustache-autoloads.el --- automatically extracted autoloads
+;;
+;;; Code:
+
+(add-to-list 'load-path (directory-file-name
+ (or (file-name-directory #$) (car load-path))))
+
+\f
+;;;### (autoloads nil "mustache" "mustache.el" (0 0 0 0))
+;;; Generated autoloads from mustache.el
+
+(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "mustache" '("mustache-")))
+
+;;;***
+\f
+;;;### (autoloads nil "mustache-lex" "mustache-lex.el" (0 0 0 0))
+;;; Generated autoloads from mustache-lex.el
+
+(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "mustache-lex" '("mst--")))
+
+;;;***
+\f
+;;;### (autoloads nil "mustache-parse" "mustache-parse.el" (0 0 0
+;;;;;; 0))
+;;; Generated autoloads from mustache-parse.el
+
+(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "mustache-parse" '("mst--")))
+
+;;;***
+\f
+;;;### (autoloads nil "mustache-render" "mustache-render.el" (0 0
+;;;;;; 0 0))
+;;; Generated autoloads from mustache-render.el
+
+(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "mustache-render" '("mst--")))
+
+;;;***
+\f
+;;;### (autoloads nil nil ("mustache-pkg.el") (0 0 0 0))
+
+;;;***
+\f
+;; Local Variables:
+;; version-control: never
+;; no-byte-compile: t
+;; no-update-autoloads: t
+;; coding: utf-8
+;; End:
+;;; mustache-autoloads.el ends here
--- /dev/null
+(eval-when-compile (require 'cl)) ;; first, second, destructuring-bind, loop
+(require 's)
+
+(defun mst--lex (template)
+ "Iterate through TEMPLATE, splitting {{ tags }} and bare strings.
+We return a list of lists: ((:text \"foo\") (:tag \"variable-name\"))"
+ ;; convert {{{foo}}} to {{& foo}}
+ (setq template (replace-regexp-in-string "{{{\\(.*?\\)}}}" "{{& \\1}}" template))
+
+ (let ((open-delimeter "{{")
+ (close-delimeter "}}")
+ (lexemes nil))
+ (while (not (s-equals? template ""))
+ (let* ((open-index (s-index-of open-delimeter template))
+ (close-index (s-index-of close-delimeter template)))
+ ;; todo: check open-index < close-index
+ ;; todo: error if we have an open and no close
+ (if (and open-index close-index)
+ ;; we have a tag
+ (let ((between-delimeters
+ (substring template (+ open-index (length open-delimeter)) close-index))
+ (continue-from-index (+ close-index (length close-delimeter))))
+ ;; save the string before the tag
+ (when (> open-index 0)
+ (push (list :text (substring template 0 open-index)) lexemes))
+
+ ;; if this is a tag that changes delimeters e.g. {{=<< >>=}}
+ ;; then set the new open/close delimeter string
+ (if (s-matches-p "^=.+ .+=$" between-delimeters)
+ (let* (;; strip leading/trailing =
+ (delimeter-spec (substring between-delimeters 1 -1))
+ (spec-parts (s-split " " delimeter-spec)))
+ (setq open-delimeter (first spec-parts))
+ (setq close-delimeter (second spec-parts)))
+
+ ;; otherwise it's a normal tag, so save it
+ (push (list :tag (s-trim between-delimeters)) lexemes))
+
+ ;; iterate on the remaining template
+ (setq template
+ (substring template continue-from-index)))
+ ;; else only plain text left
+ (progn
+ (push (list :text template) lexemes)
+ (setq template "")))))
+ (nreverse lexemes)))
+
+(defun mst--clean-whitespace (lexemes)
+ "Given a list of LEXEMES, remove whitespace around sections and
+comments if they're on their own on a line. Modifies the original
+list."
+ ;; iterate over all lexemes:
+ (loop for i from 0 to (- (length lexemes) 3)
+ collect
+ ;; find sections that have plain text before and after
+ (let ((first (elt lexemes i))
+ (second (elt lexemes (+ i 1)))
+ (third (elt lexemes (+ i 2))))
+ (when (and (mst--text-p first)
+ (or
+ (mst--section-tag-p second)
+ (mst--comment-tag-p second))
+ (mst--text-p third)
+ ;; check the section is on its own line
+ (string-match-p "\n *$" (mst--tag-text first))
+ (string-match-p "^\n" (mst--tag-text third)))
+ ;; then we cleanup whitespace
+ (setf (elt lexemes i) (mst--no-trailing-newline first)))))
+ lexemes)
+
+(defalias 'mst--tag-text 'second
+ "Returns the text context of a tag.")
+
+(defun mst--no-trailing-newline (lexeme)
+ "Replace \"\n\" or \"\n \" at the end of a plain text lexeme."
+ (list
+ :text
+ (replace-regexp-in-string "\n *$" "" (mst--tag-text lexeme))))
+
+(defun mst--tag-p (lexeme)
+ "Is LEXEME a tag?"
+ (equal (car lexeme) :tag))
+
+(defun mst--section-tag-p (lexeme)
+ "Is LEXEME a section tag?"
+ (or
+ (mst--open-section-tag-p lexeme)
+ (mst--inverted-section-tag-p lexeme)
+ (mst--close-section-tag-p lexeme)))
+
+(defun mst--open-section-tag-p (lexeme)
+ "Is LEXEME an open section tag?
+See also `mst--inverted-section-tag-p'."
+ (and (mst--tag-p lexeme)
+ (s-starts-with-p "#" (second lexeme))))
+
+(defun mst--inverted-section-tag-p (lexeme)
+ "Is LEXEME an inverted section tag?"
+ (and (mst--tag-p lexeme)
+ (s-starts-with-p "^" (second lexeme))))
+
+(defun mst--close-section-tag-p (lexeme)
+ "Is LEXEME a close section tag?"
+ (and (mst--tag-p lexeme)
+ (s-starts-with-p "/" (second lexeme))))
+
+(defun mst--comment-tag-p (lexeme)
+ "Is LEXEME a comment tag?"
+ (and (mst--tag-p lexeme)
+ (s-starts-with-p "!" (second lexeme))))
+
+(defun mst--unescaped-tag-p (lexeme)
+ "Is LEXEME an unescaped variable tag?
+Note that the lexer converts {{{foo}}} to {{& foo}}."
+ (and (mst--tag-p lexeme)
+ (s-starts-with-p "&" (second lexeme))))
+
+(defun mst--partial-tag-p (lexeme)
+ "Is LEXEME a partial tag?"
+ (and (mst--tag-p lexeme)
+ (s-starts-with-p ">" (second lexeme))))
+
+(defun mst--section-p (lexeme)
+ "Is LEXEME a nested section?"
+ (not (atom (car lexeme))))
+
+(defun mst--text-p (lexeme)
+ "Is LEXEME plain text?"
+ (equal (car lexeme) :text))
+
+;; fixme: assumes the delimeters haven't changed
+;; fixme: mst--lex doens't preserve whitespace
+(defun mst--unlex (lexemes)
+ "Given a lexed (and optionally parsed) list of lexemes,
+return the original input string."
+ (if lexemes
+ (let ((lexeme (first lexemes))
+ (rest (cdr lexemes)))
+ (cond
+ ;; recurse on this section, then the rest
+ ((mst--section-p lexeme)
+ (concat (mst--unlex lexeme) (mst--unlex rest)))
+ ((mst--tag-p lexeme)
+ ;; restore the delimeters, then unlex the rest
+ (let ((tag-name (second lexeme)))
+ (concat "{{" tag-name "}}" (mst--unlex rest))))
+ ;; otherwise, it's just raw text
+ (t
+ (let ((text (second lexeme)))
+ (concat text (mst--unlex rest))))))
+ ""))
--- /dev/null
+(require 's)
+(eval-when-compile (require 'cl)) ;; loop, return
+
+(defvar mst--remaining-lexemes nil
+ "Since `mst--parse-inner' recursively calls itself, we need a shared value to mutate.")
+
+(defun mst--parse (lexemes)
+ "Given a list LEXEMES, return a list of lexemes nested according to #tags or ^tags."
+ (setq mst--remaining-lexemes lexemes)
+ (mst--parse-inner))
+
+(defun mst--parse-inner (&optional section-name)
+ "Parse `mst--remaining-lexemes', and return a list of lexemes nested according to #tags or ^tags."
+ (let (parsed-lexemes
+ lexeme)
+ (loop while mst--remaining-lexemes do
+ (setq lexeme (pop mst--remaining-lexemes))
+ (cond
+ ((mst--open-section-p lexeme)
+ ;; recurse on this nested section
+ (push (cons lexeme (mst--parse-inner (mst--section-name lexeme))) parsed-lexemes))
+ ((mst--close-section-p lexeme)
+ ;; this is the last tag in this section
+ (unless (equal section-name (mst--section-name lexeme))
+ (error "Mismatched brackets: You closed a section with %s, but it wasn't open" section-name))
+ (push lexeme parsed-lexemes)
+ (return))
+ (t
+ ;; this is just a tag in the current section
+ (push lexeme parsed-lexemes))))
+
+ ;; ensure we aren't inside an unclosed section
+ (when (and section-name (not (mst--close-section-p lexeme)))
+ (error "Unclosed section: You haven't closed %s" section-name))
+
+ (nreverse parsed-lexemes)))
+
+(defun mst--open-section-p (lexeme)
+ "Is LEXEME a #tag or ^tag ?"
+ (destructuring-bind (type value) lexeme
+ (and (equal type :tag)
+ (or
+ (s-starts-with-p "#" value)
+ (s-starts-with-p "^" value)))))
+
+(defun mst--close-section-p (lexeme)
+ "Is LEXEME a /tag ?"
+ (destructuring-bind (type value) lexeme
+ (and (equal type :tag)
+ (s-starts-with-p "/" value))))
+
+(defun mst--section-name (lexeme)
+ "Get the name of the section from LEXEME, a two part list returned by `mst--lex'.
+The leading character (the #, ^ or /) is stripped."
+ (s-chop-prefixes '("#" "^" "/") (cadr lexeme)))
--- /dev/null
+(define-package "mustache" "20170923.1233" "a mustache templating library in emacs lisp"
+ '((ht "0.9")
+ (s "1.3.0")
+ (dash "1.2.0")))
+;; Local Variables:
+;; no-byte-compile: t
+;; End:
--- /dev/null
+(require 'ht)
+(require 's)
+(require 'dash)
+
+(eval-when-compile '(require 'cl)) ;; return, dolist
+
+(load "mustache-lex.el")
+(load "mustache-parse.el")
+
+(defun mst--render (template context)
+ "Render a mustache TEMPLATE with hash table CONTEXT."
+ (-> template
+ mst--lex
+ mst--clean-whitespace
+ mst--parse
+ (mst--render-section-list context)))
+
+(defun mst--mapconcat (function sequence)
+ "Apply FUNCTION to every element in SEQUENCE, and concat the results as strings."
+ (mapconcat function sequence ""))
+
+(defmacro mst--amapconcat (form sequence)
+ "Anaphoric version of `mst--mapconcat'."
+ `(mst--mapconcat (lambda (it) ,form) ,sequence))
+
+;; todo: set flag to set tolerance of missing templates
+(defun mst--get-partial (name)
+ "Get the first partial whose file name is NAME.mustache, or nil otherwise.
+Partials are searched for in `mustache-partial-paths'."
+ (unless (listp mustache-partial-paths)
+ (error "`mustache-partial-paths' must be a list of paths"))
+ (let ((partial-name (format "%s.mustache" name)))
+ (dolist (path mustache-partial-paths)
+ (-when-let*
+ ((partials (directory-files path nil "\\.mustache$"))
+ (matching-partial (--first
+ (string-match-p (regexp-quote partial-name) it)
+ partials)))
+ (return
+ (with-temp-buffer
+ (insert-file-contents-literally (expand-file-name matching-partial path))
+ (buffer-substring-no-properties (point-min) (point-max))))))))
+
+(defun mst--render-section-list (sections context)
+ "Render a parsed list SECTIONS in CONTEXT."
+ (mst--amapconcat (mst--render-section it context) sections))
+
+(defun mst--context-get (context variable-name &optional default)
+ "Lookup VARIABLE-NAME in CONTEXT, returning DEFAULT if not present."
+ (when (eq mustache-key-type 'keyword)
+ (setq variable-name (intern (concat ":" variable-name))))
+ (ht-get context variable-name default))
+
+(defun mst--render-tag (parsed-tag context)
+ "Given PARSED-TAG, render it in hash table CONTEXT."
+ (let ((inner-text (mst--tag-text parsed-tag)))
+ (cond
+ ((mst--comment-tag-p parsed-tag)
+ "")
+ ((mst--unescaped-tag-p parsed-tag)
+ (let ((variable-value (mst--context-get context (s-trim (substring inner-text 1)) "")))
+ (when (numberp variable-value)
+ (setq variable-value (number-to-string variable-value)))
+ variable-value))
+ ((mst--partial-tag-p parsed-tag)
+ (let ((partial (mst--get-partial (s-trim (substring inner-text 1)))))
+ (if partial
+ (mst--render partial context)
+ "")))
+ (t ;; normal variable
+ (let ((variable-value (mst--context-get context inner-text "")))
+ (when (numberp variable-value)
+ (setq variable-value (number-to-string variable-value)))
+ (mst--escape-html variable-value))))))
+
+(defun mst--context-add (table from-table)
+ "Return a copy of TABLE where all the key-value pairs in FROM-TABLE have been set."
+ (let ((new-table (ht-copy table)))
+ (ht-update new-table from-table)
+ new-table))
+
+(defun mst--listp (object)
+ "Return t if OBJECT is a list.
+Unlike `listp', does not return t if OBJECT is a function."
+ (and (not (functionp object)) (listp object)))
+
+(defun mst--section-name (section-tag)
+ "Get the name of this SECCTION-TAG.
+E.g. from {{#foo}} to \"foo\"."
+ (-> section-tag ;; e.g (:tag "#foo")
+ mst--tag-text ;; to "#foo"
+ (substring 1) ;; to "foo"
+ s-trim))
+
+(defun mst--render-section (parsed-lexeme context)
+ "Given PARSED-LEXEME -- a lexed tag, plain text, or a nested list,
+render it in CONTEXT."
+ (cond ((mst--section-p parsed-lexeme)
+ ;; nested section
+ (let* ((section-tag (first parsed-lexeme))
+ (section-name (mst--section-name section-tag))
+ (context-value (mst--context-get context section-name))
+ ;; strip section open and close
+ (section-contents (-slice parsed-lexeme 1 -1)))
+ (cond
+ ((mst--open-section-tag-p section-tag)
+ (cond
+ ;; if the context is a list of hash tables, render repeatedly
+ ((or (mst--listp context-value) (vectorp context-value))
+ (mst--amapconcat (mst--render-section-list section-contents (mst--context-add context it)) context-value))
+ ;; if the context is a hash table, render in that context
+ ((hash-table-p context-value)
+ (mst--render-section-list section-contents (mst--context-add context context-value)))
+ ;; if the context is a function, call it
+ ((functionp context-value)
+ (funcall context-value (mst--unlex section-contents) context))
+ ;; if it's a truthy value, render in the current context
+ (context-value
+ (mst--render-section-list section-contents context))
+ ;; otherwise, don't render anything
+ (t "")))
+ ;; render ^tags
+ ((mst--inverted-section-tag-p section-tag)
+ (if context-value
+ ""
+ (mst--render-section-list section-contents context))))))
+ ((mst--tag-p parsed-lexeme)
+ (mst--render-tag parsed-lexeme context))
+ ;; plain text
+ (t
+ (second parsed-lexeme))))
+
+(defun mst--escape-html (string)
+ "Escape HTML in STRING."
+ (->> string
+ (s-replace "&" "&")
+ (s-replace "<" "<")
+ (s-replace ">" ">")
+ (s-replace "'" "'")
+ (s-replace "\"" """)))
--- /dev/null
+;;; mustache.el --- a mustache templating library in emacs lisp
+
+;; Copyright (C) 2013 Wilfred Hughes
+
+;; Author: Wilfred Hughes <meNOSPAMwilfred.me.uk>
+;; Version: 0.24
+;; Keywords: mustache, template
+;; Package-Requires: ((ht "0.9") (s "1.3.0") (dash "1.2.0"))
+
+;; This program 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 3 of the License, or
+;; (at your option) any later version.
+
+;; This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; See documentation at https://github.com/Wilfred/mustache.el
+
+;; Note on terminology: We treat mustache templates as a sequence of
+;; strings (plain text), and tags (anything wrapped in delimeters:
+;; {{foo}}). A section is a special tag that requires closing
+;; (e.g. {{#foo}}{{/foo}}).
+
+;; We treat mustache templates as if they conform to a rough grammar:
+
+;; TEMPLATE = plaintext | TAG | SECTION | TEMPLATE TEMPLATE
+;; SECTION = OPEN-TAG TEMPLATE CLOSE-TAG
+;; TAG = "{{" text "}}"
+
+;; Public functions are of the form `mustache-FOO`, private
+;; functions/variables are of the form `mst--FOO`.
+
+;;; Code:
+
+(load "mustache-render.el")
+
+;; todo: add flag to set tolerance of missing variables
+(defun mustache-render (template context)
+ "Render a mustache TEMPLATE with hash table CONTEXT."
+ (mst--render template context))
+
+(defvar mustache-partial-paths nil
+ "A list of paths to be searched for mustache partial templates (files ending .mustache).")
+
+(defvar mustache-key-type 'string
+ "What type of key we expect in contexts.
+Can take the value 'string or 'keyword.
+
+For 'string we expect contexts of the form:
+#s\(hash-table data \(\"name\" \"J. Random user\"\)\)
+
+for 'keyword we expect contexts of the form:
+#s\(hash-table data \(:name \"J. Random user\"\)\)
+")
+
+(provide 'mustache)
+;;; mustache.el ends here
--- /dev/null
+;;; mustache-mode-autoloads.el --- automatically extracted autoloads
+;;
+;;; Code:
+
+(add-to-list 'load-path (directory-file-name
+ (or (file-name-directory #$) (car load-path))))
+
+\f
+;;;### (autoloads nil "mustache-mode" "mustache-mode.el" (0 0 0 0))
+;;; Generated autoloads from mustache-mode.el
+
+(autoload 'mustache-mode "mustache-mode" "\
+
+
+\(fn)" t nil)
+
+(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "mustache-mode" '("mustache-")))
+
+;;;***
+\f
+;; Local Variables:
+;; version-control: never
+;; no-byte-compile: t
+;; no-update-autoloads: t
+;; coding: utf-8
+;; End:
+;;; mustache-mode-autoloads.el ends here
--- /dev/null
+;;; -*- no-byte-compile: t -*-
+(define-package "mustache-mode" "20141024.1432" "A major mode for editing Mustache files." 'nil :commit "bf9897eb287ca47ced65d7d4e07ea61ea0aec39f" :authors '(("Tony Gentilcore") ("Chris Wanstrath") ("Daniel Hackney")) :maintainer '("Tony Gentilcore"))
--- /dev/null
+;;; mustache-mode.el --- A major mode for editing Mustache files.
+
+;; Author: Tony Gentilcore
+;; Chris Wanstrath
+;; Daniel Hackney
+
+;; Version: 1.3
+;; Package-Version: 20141024.1432
+
+;; This file is not part of Emacs
+
+;; 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:
+
+;; 1) Copy this file somewhere in your Emacs `load-path'. To see what
+;; your `load-path' is, run inside emacs: C-h v load-path<RET>
+;;
+;; 2) Add the following to your .emacs file:
+;;
+;; (require 'mustache-mode)
+
+;; While the Mustache language can be used for any types of text,
+;; this mode is intended for using Mustache to write HTML.
+
+;;; Known Bugs:
+
+;; The indentation still has minor bugs due to the fact that
+;; templates do not require valid HTML.
+
+;; It would be nice to be able to highlight attributes of HTML tags,
+;; however this is difficult due to the presence of CTemplate symbols
+;; embedded within attributes.
+
+(eval-when-compile
+ (require 'font-lock))
+
+(defvar mustache-mode-version "1.3"
+ "Version of `mustache-mode.el'.")
+
+(defvar mustache-mode-map
+ (let ((map (make-sparse-keymap)))
+ (define-key map "\C-m" 'reindent-then-newline-and-indent)
+ (define-key map "\C-ct" 'mustache-insert-tag)
+ (define-key map "\C-cv" 'mustache-insert-variable)
+ (define-key map "\C-cs" 'mustache-insert-section)
+ map)
+ "Keymap for mustache-mode major mode")
+
+(defvar mustache-mode-syntax-table
+ (let ((st (make-syntax-table)))
+ (modify-syntax-entry ?< "(> " st)
+ (modify-syntax-entry ?> ")< " st)
+ (modify-syntax-entry ?\" ". " st)
+ (modify-syntax-entry ?\\ ". " st)
+ (modify-syntax-entry ?' "w " st)
+ st)
+ "Syntax table in use in mustache-mode buffers.")
+
+(defvar mustache-basic-offset 2
+ "The basic indentation offset.")
+
+;; Constant regular expressions to identify template elements.
+(defconst mustache-mode-mustache-token "[a-zA-Z_.][a-zA-Z0-9_:=\?!.-]*?")
+(defconst mustache-mode-section (concat "\\({{[#^/]\s*"
+ mustache-mode-mustache-token
+ "\s*}}\\)"))
+(defconst mustache-mode-open-section (concat "\\({{[#^]\s*"
+ mustache-mode-mustache-token
+ "\s*}}\\)"))
+(defconst mustache-mode-close-section (concat "{{/\\(\s*"
+ mustache-mode-mustache-token
+ "\s*\\)}}"))
+(defconst mustache-mode-comment "\\({{!\\(?:.\\|\n\\)*?}}\\)")
+(defconst mustache-mode-include (concat "\\({{[><]\s*"
+ mustache-mode-mustache-token
+ "\s*}}\\)"))
+(defconst mustache-mode-variable (concat "\\({{\s*"
+ mustache-mode-mustache-token
+ "\s*}}\\)"))
+(defconst mustache-mode-variable (concat "\\({{{?\s*"
+ mustache-mode-mustache-token
+ "\s*}}}?\\)"))
+(defconst mustache-mode-builtins
+ (concat
+ "\\({{\\<\s*"
+ (regexp-opt
+ '("BI_NEWLINE" "BI_SPACE")
+ t)
+ "\s*\\>}}\\)"))
+(defconst mustache-mode-close-section-at-start (concat "^[ \t]*?"
+ mustache-mode-close-section))
+
+;; Constant regular expressions to identify html tags.
+;; Taken from HTML 4.01 / XHTML 1.0 Reference found at:
+;; http://www.w3schools.com/tags/default.asp.
+(defconst mustache-mode-html-constant "\\(&#?[a-z0-9]\\{2,5\\};\\)")
+(defconst mustache-mode-pair-tag
+ (concat
+ "\\<"
+ (regexp-opt
+ '("a" "abbr" "acronym" "address" "applet" "area" "b" "bdo"
+ "big" "blockquote" "body" "button" "caption" "center" "cite"
+ "code" "col" "colgroup" "dd" "del" "dfn" "dif" "div" "dl"
+ "dt" "em" "fieldset" "font" "form" "frame" "frameset" "h1"
+ "header" "nav" "footer" "section"
+ "h2" "h3" "h4" "h5" "h6" "head" "html" "i" "iframe" "ins"
+ "kbd" "label" "legend" "li" "link" "map" "menu" "noframes"
+ "noscript" "object" "ol" "optgroup" "option" "p" "pre" "q"
+ "s" "samp" "script" "select" "small" "span" "strike"
+ "strong" "style" "sub" "sup" "table" "tbody" "td" "textarea"
+ "tfoot" "th" "thead" "title" "tr" "tt" "u" "ul" "var")
+ t)
+ "\\>"))
+(defconst mustache-mode-standalone-tag
+ (concat
+ "\\<"
+ (regexp-opt
+ '("base" "br" "hr" "img" "input" "meta" "param")
+ t)
+ "\\>"))
+(defconst mustache-mode-open-tag (concat "<\\("
+ mustache-mode-pair-tag
+ "\\)"))
+(defconst mustache-mode-close-tag (concat "</\\("
+ mustache-mode-pair-tag
+ "\\)>"))
+(defconst mustache-mode-close-tag-at-start (concat "^[ \t]*?"
+ mustache-mode-close-tag))
+
+(defconst mustache-mode-blank-line "^[ \t]*?$")
+(defconst mustache-mode-dangling-open (concat "\\("
+ mustache-mode-open-section
+ "\\)\\|\\("
+ mustache-mode-open-tag
+ "\\)[^/]*$"))
+
+(defun mustache-insert-tag (tag)
+ "Inserts an HTML tag."
+ (interactive "sTag: ")
+ (mustache-indent)
+ (insert (concat "<" tag ">"))
+ (insert "\n\n")
+ (insert (concat "</" tag ">"))
+ (mustache-indent)
+ (forward-line -1)
+ (mustache-indent))
+
+(defun mustache-insert-variable (variable)
+ "Inserts a tpl variable."
+ (interactive "sVariable: ")
+ (insert (concat "{{" variable "}}")))
+
+(defun mustache-insert-section (section)
+ "Inserts a tpl section."
+ (interactive "sSection: ")
+ (mustache-indent)
+ (insert (concat "{{#" section "}}\n"))
+ (insert "\n")
+ (insert (concat "{{/" section "}}"))
+ (mustache-indent)
+ (forward-line -1)
+ (mustache-indent))
+
+(defun mustache-indent ()
+ "Indent current line"
+ ;; Set the point to beginning of line.
+ (beginning-of-line)
+ ;; If we are at the beginning of the file, indent to 0.
+ (if (bobp)
+ (indent-line-to 0)
+ (let ((tag-stack 1) (close-tag "") (cur-indent 0) (old-pnt (point-marker))
+ (close-at-start) (open-token) (dangling-open))
+ (progn
+ ;; Determine if this is a template line or an html line.
+ (if (looking-at "^[ \t]*?{{")
+ (setq close-at-start mustache-mode-close-section-at-start
+ open-token "{{#")
+ (setq close-at-start mustache-mode-close-tag-at-start
+ open-token "<"))
+ ;; If there is a closing tag at the start of the line, search back
+ ;; for its opener and indent to that level.
+ (if (looking-at close-at-start)
+ (progn
+ (save-excursion
+ (setq close-tag (match-string 1))
+ ;; Keep searching for a match for the close tag until
+ ;; the tag-stack is 0.
+ (while (and (not (bobp))
+ (> tag-stack 0)
+ (re-search-backward (concat open-token
+ "\\(/?\\)"
+ close-tag) nil t))
+ (if (string-equal (match-string 1) "/")
+ ;; We found another close tag, so increment tag-stack.
+ (setq tag-stack (+ tag-stack 1))
+ ;; We found an open tag, so decrement tag-stack.
+ (setq tag-stack (- tag-stack 1)))
+ (setq cur-indent (current-indentation))))
+ (if (> tag-stack 0)
+ (save-excursion
+ (forward-line -1)
+ (setq cur-indent (current-indentation)))))
+ ;; This was not a closing tag, so we check if the previous line
+ ;; was an opening tag.
+ (save-excursion
+ ;; Keep moving back until we find a line that is not blank
+ (while (progn
+ (forward-line -1)
+ (and (not (bobp)) (looking-at mustache-mode-blank-line))))
+ (setq cur-indent (current-indentation))
+ (if (re-search-forward mustache-mode-dangling-open old-pnt t)
+ (setq cur-indent (+ cur-indent mustache-basic-offset)))))
+ ;; Finally, we execute the actual indentation.
+ (if (> cur-indent 0)
+ (indent-line-to cur-indent)
+ (indent-line-to 0))))))
+
+(defconst mustache-mode-font-lock-keywords
+ `((,mustache-mode-section (1 font-lock-keyword-face))
+ (,mustache-mode-comment (1 font-lock-comment-face))
+ (,mustache-mode-include (1 font-lock-function-name-face))
+ (,mustache-mode-builtins (1 font-lock-variable-name-face))
+ (,mustache-mode-variable (1 font-lock-reference-face))
+ (,(concat "</?\\(" mustache-mode-pair-tag "\\)") (1 font-lock-function-name-face))
+ (,(concat "<\\(" mustache-mode-standalone-tag "\\)") (1 font-lock-function-name-face))
+ (,mustache-mode-html-constant (1 font-lock-variable-name-face))))
+
+;;;###autoload
+(define-derived-mode mustache-mode fundamental-mode "Mustache"
+ (set (make-local-variable 'indent-line-function) 'mustache-indent)
+ (set (make-local-variable 'indent-tabs-mode) nil)
+ (set (make-local-variable 'font-lock-defaults) '(mustache-mode-font-lock-keywords)))
+
+(add-to-list 'auto-mode-alist '("\\.mustache$" . mustache-mode))
+
+(provide 'mustache-mode)
+
+;;; mustache-mode.el ends here
+++ /dev/null
-;;; mustache-mode-autoloads.el --- automatically extracted autoloads
-;;
-;;; Code:
-(add-to-list 'load-path (or (file-name-directory #$) (car load-path)))
-\f
-;;;### (autoloads nil "mustache-mode" "mustache-mode.el" (21908 14210
-;;;;;; 0 0))
-;;; Generated autoloads from mustache-mode.el
-
-(autoload 'mustache-mode "mustache-mode" "\
-
-
-\(fn)" t nil)
-
-;;;***
-\f
-;; Local Variables:
-;; version-control: never
-;; no-byte-compile: t
-;; no-update-autoloads: t
-;; End:
-;;; mustache-mode-autoloads.el ends here
+++ /dev/null
-(define-package "mustache-mode" "20141024.732" "A major mode for editing Mustache files." 'nil)
+++ /dev/null
-;;; mustache-mode.el --- A major mode for editing Mustache files.
-
-;; Author: Tony Gentilcore
-;; Chris Wanstrath
-;; Daniel Hackney
-
-;; Version: 1.3
-;; Package-Version: 20141024.732
-
-;; This file is not part of Emacs
-
-;; 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:
-
-;; 1) Copy this file somewhere in your Emacs `load-path'. To see what
-;; your `load-path' is, run inside emacs: C-h v load-path<RET>
-;;
-;; 2) Add the following to your .emacs file:
-;;
-;; (require 'mustache-mode)
-
-;; While the Mustache language can be used for any types of text,
-;; this mode is intended for using Mustache to write HTML.
-
-;;; Known Bugs:
-
-;; The indentation still has minor bugs due to the fact that
-;; templates do not require valid HTML.
-
-;; It would be nice to be able to highlight attributes of HTML tags,
-;; however this is difficult due to the presence of CTemplate symbols
-;; embedded within attributes.
-
-(eval-when-compile
- (require 'font-lock))
-
-(defvar mustache-mode-version "1.3"
- "Version of `mustache-mode.el'.")
-
-(defvar mustache-mode-map
- (let ((map (make-sparse-keymap)))
- (define-key map "\C-m" 'reindent-then-newline-and-indent)
- (define-key map "\C-ct" 'mustache-insert-tag)
- (define-key map "\C-cv" 'mustache-insert-variable)
- (define-key map "\C-cs" 'mustache-insert-section)
- map)
- "Keymap for mustache-mode major mode")
-
-(defvar mustache-mode-syntax-table
- (let ((st (make-syntax-table)))
- (modify-syntax-entry ?< "(> " st)
- (modify-syntax-entry ?> ")< " st)
- (modify-syntax-entry ?\" ". " st)
- (modify-syntax-entry ?\\ ". " st)
- (modify-syntax-entry ?' "w " st)
- st)
- "Syntax table in use in mustache-mode buffers.")
-
-(defvar mustache-basic-offset 2
- "The basic indentation offset.")
-
-;; Constant regular expressions to identify template elements.
-(defconst mustache-mode-mustache-token "[a-zA-Z_.][a-zA-Z0-9_:=\?!.-]*?")
-(defconst mustache-mode-section (concat "\\({{[#^/]\s*"
- mustache-mode-mustache-token
- "\s*}}\\)"))
-(defconst mustache-mode-open-section (concat "\\({{[#^]\s*"
- mustache-mode-mustache-token
- "\s*}}\\)"))
-(defconst mustache-mode-close-section (concat "{{/\\(\s*"
- mustache-mode-mustache-token
- "\s*\\)}}"))
-(defconst mustache-mode-comment "\\({{!\\(?:.\\|\n\\)*?}}\\)")
-(defconst mustache-mode-include (concat "\\({{[><]\s*"
- mustache-mode-mustache-token
- "\s*}}\\)"))
-(defconst mustache-mode-variable (concat "\\({{\s*"
- mustache-mode-mustache-token
- "\s*}}\\)"))
-(defconst mustache-mode-variable (concat "\\({{{?\s*"
- mustache-mode-mustache-token
- "\s*}}}?\\)"))
-(defconst mustache-mode-builtins
- (concat
- "\\({{\\<\s*"
- (regexp-opt
- '("BI_NEWLINE" "BI_SPACE")
- t)
- "\s*\\>}}\\)"))
-(defconst mustache-mode-close-section-at-start (concat "^[ \t]*?"
- mustache-mode-close-section))
-
-;; Constant regular expressions to identify html tags.
-;; Taken from HTML 4.01 / XHTML 1.0 Reference found at:
-;; http://www.w3schools.com/tags/default.asp.
-(defconst mustache-mode-html-constant "\\(&#?[a-z0-9]\\{2,5\\};\\)")
-(defconst mustache-mode-pair-tag
- (concat
- "\\<"
- (regexp-opt
- '("a" "abbr" "acronym" "address" "applet" "area" "b" "bdo"
- "big" "blockquote" "body" "button" "caption" "center" "cite"
- "code" "col" "colgroup" "dd" "del" "dfn" "dif" "div" "dl"
- "dt" "em" "fieldset" "font" "form" "frame" "frameset" "h1"
- "header" "nav" "footer" "section"
- "h2" "h3" "h4" "h5" "h6" "head" "html" "i" "iframe" "ins"
- "kbd" "label" "legend" "li" "link" "map" "menu" "noframes"
- "noscript" "object" "ol" "optgroup" "option" "p" "pre" "q"
- "s" "samp" "script" "select" "small" "span" "strike"
- "strong" "style" "sub" "sup" "table" "tbody" "td" "textarea"
- "tfoot" "th" "thead" "title" "tr" "tt" "u" "ul" "var")
- t)
- "\\>"))
-(defconst mustache-mode-standalone-tag
- (concat
- "\\<"
- (regexp-opt
- '("base" "br" "hr" "img" "input" "meta" "param")
- t)
- "\\>"))
-(defconst mustache-mode-open-tag (concat "<\\("
- mustache-mode-pair-tag
- "\\)"))
-(defconst mustache-mode-close-tag (concat "</\\("
- mustache-mode-pair-tag
- "\\)>"))
-(defconst mustache-mode-close-tag-at-start (concat "^[ \t]*?"
- mustache-mode-close-tag))
-
-(defconst mustache-mode-blank-line "^[ \t]*?$")
-(defconst mustache-mode-dangling-open (concat "\\("
- mustache-mode-open-section
- "\\)\\|\\("
- mustache-mode-open-tag
- "\\)[^/]*$"))
-
-(defun mustache-insert-tag (tag)
- "Inserts an HTML tag."
- (interactive "sTag: ")
- (mustache-indent)
- (insert (concat "<" tag ">"))
- (insert "\n\n")
- (insert (concat "</" tag ">"))
- (mustache-indent)
- (forward-line -1)
- (mustache-indent))
-
-(defun mustache-insert-variable (variable)
- "Inserts a tpl variable."
- (interactive "sVariable: ")
- (insert (concat "{{" variable "}}")))
-
-(defun mustache-insert-section (section)
- "Inserts a tpl section."
- (interactive "sSection: ")
- (mustache-indent)
- (insert (concat "{{#" section "}}\n"))
- (insert "\n")
- (insert (concat "{{/" section "}}"))
- (mustache-indent)
- (forward-line -1)
- (mustache-indent))
-
-(defun mustache-indent ()
- "Indent current line"
- ;; Set the point to beginning of line.
- (beginning-of-line)
- ;; If we are at the beginning of the file, indent to 0.
- (if (bobp)
- (indent-line-to 0)
- (let ((tag-stack 1) (close-tag "") (cur-indent 0) (old-pnt (point-marker))
- (close-at-start) (open-token) (dangling-open))
- (progn
- ;; Determine if this is a template line or an html line.
- (if (looking-at "^[ \t]*?{{")
- (setq close-at-start mustache-mode-close-section-at-start
- open-token "{{#")
- (setq close-at-start mustache-mode-close-tag-at-start
- open-token "<"))
- ;; If there is a closing tag at the start of the line, search back
- ;; for its opener and indent to that level.
- (if (looking-at close-at-start)
- (progn
- (save-excursion
- (setq close-tag (match-string 1))
- ;; Keep searching for a match for the close tag until
- ;; the tag-stack is 0.
- (while (and (not (bobp))
- (> tag-stack 0)
- (re-search-backward (concat open-token
- "\\(/?\\)"
- close-tag) nil t))
- (if (string-equal (match-string 1) "/")
- ;; We found another close tag, so increment tag-stack.
- (setq tag-stack (+ tag-stack 1))
- ;; We found an open tag, so decrement tag-stack.
- (setq tag-stack (- tag-stack 1)))
- (setq cur-indent (current-indentation))))
- (if (> tag-stack 0)
- (save-excursion
- (forward-line -1)
- (setq cur-indent (current-indentation)))))
- ;; This was not a closing tag, so we check if the previous line
- ;; was an opening tag.
- (save-excursion
- ;; Keep moving back until we find a line that is not blank
- (while (progn
- (forward-line -1)
- (and (not (bobp)) (looking-at mustache-mode-blank-line))))
- (setq cur-indent (current-indentation))
- (if (re-search-forward mustache-mode-dangling-open old-pnt t)
- (setq cur-indent (+ cur-indent mustache-basic-offset)))))
- ;; Finally, we execute the actual indentation.
- (if (> cur-indent 0)
- (indent-line-to cur-indent)
- (indent-line-to 0))))))
-
-(defconst mustache-mode-font-lock-keywords
- `((,mustache-mode-section (1 font-lock-keyword-face))
- (,mustache-mode-comment (1 font-lock-comment-face))
- (,mustache-mode-include (1 font-lock-function-name-face))
- (,mustache-mode-builtins (1 font-lock-variable-name-face))
- (,mustache-mode-variable (1 font-lock-reference-face))
- (,(concat "</?\\(" mustache-mode-pair-tag "\\)") (1 font-lock-function-name-face))
- (,(concat "<\\(" mustache-mode-standalone-tag "\\)") (1 font-lock-function-name-face))
- (,mustache-mode-html-constant (1 font-lock-variable-name-face))))
-
-;;;###autoload
-(define-derived-mode mustache-mode fundamental-mode "Mustache"
- (set (make-local-variable 'indent-line-function) 'mustache-indent)
- (set (make-local-variable 'indent-tabs-mode) nil)
- (set (make-local-variable 'font-lock-defaults) '(mustache-mode-font-lock-keywords)))
-
-(add-to-list 'auto-mode-alist '("\\.mustache$" . mustache-mode))
-
-(provide 'mustache-mode)
-
-;;; mustache-mode.el ends here