To install this package, run in Emacs:
M-x package-install RET tempel RET
Tempel is a tiny template package for Emacs, which uses the syntax of the Emacs Tempo library. Tempo is an ancient temple of the church of Emacs. It is 27 years old, but still in good shape since it successfully resisted change over the decades. However it may look a bit dusty here and there. Therefore we present to you, Tempel, a modernized implementation of Tempo, in the form of three commands:
tempel-complete
completes a template name at point in the buffer and
subsequently expands the template. If called non-interactively the function
behaves like a Capf and can be added to completion-at-point-functions
.tempel-expand
expands an exactly matching template name at point in the buffer.
If called non-interactively the function behaves like a Capf and can be added
to completion-at-point-functions
.tempel-insert
selects a template by name and insert it into the current buffer.
For the completion at point commands tempel-complete
and tempel-expand
, you may
want to give my Corfu completion at point popup UI a try. After inserting the
template you can move between the visible template fields with the keys M-{
, M-}
or C-up/down
which are normally bound to forward/backward-paragraph
. Tempel
temporarily remaps these commands to tempel-next/previous
. The key
bindings are defined in the tempel-map
keymap. You can customize them there. As
soon as you move before (behind) the first (last) field, the fields are
finalized.
Tempel can hook into the abbrev mechanism of Emacs by enabling the
tempel-abbrev-mode
in a buffer or by enabling the tempel-global-abbrev-mode
.
Then the Tempel templates will be available via expand-abbrev
which is usually
bound to C-x '
.
Note that this package is not a competitor to the mature and widely used YASnippet library, which comes with many readily available snippet collections. Try Tempel only if you like small and simple packages. With Tempel you write your templates in Lisp syntax, which from my perspective fits well to the hackable nature of Emacs. Tempel took inspiration from the Tempo-Snippets package by Nikolaj Schumacher (GitHub link).
![]() | ![]() | ![]() |
The package is available on GNU ELPA and MELPA and can be installed with
package-install
. In the following we show an example configuration, which relies
on on use-package
.
;; Configure Tempel (use-package tempel :bind (("M-+" . tempel-complete) ;; Alternative tempel-expand ("M-*" . tempel-insert)) :init ;; Setup completion at point (defun tempel-setup-capf () ;; Add the Tempel Capf to `completion-at-point-functions'. `tempel-expand' ;; only triggers on exact matches. Alternatively use `tempel-complete' if ;; you want to see all matches, but then Tempel will probably trigger too ;; often when you don't expect it. ;; NOTE: We add `tempel-expand' *before* the main programming mode Capf, ;; such that it will be tried first. (setq-local completion-at-point-functions (cons #'tempel-expand completion-at-point-functions))) (add-hook 'prog-mode-hook 'tempel-setup-capf) (add-hook 'text-mode-hook 'tempel-setup-capf) ;; Optionally make the Tempel templates available to Abbrev, ;; either locally or globally. `expand-abbrev' is bound to C-x '. ;; (add-hook 'prog-mode-hook #'tempel-abbrev-mode) ;; (tempel-global-abbrev-mode) ) ;; Optional: Use the Corfu completion UI (use-package corfu :init (corfu-global-mode))
The templates are defined in a Lisp file which is stored by default in the
user-emacs-directory
(~/.config/emacs/templates
). The templates are grouped by
major mode with an optional :condition
. Each template is a list in the concise
form of the Emacs Tempo syntax. The first element of each list is the name of
the template. Behind the name, the Tempo syntax elements follow. Pre- and
post-expansion operations can be specified per template by the optional keys
:pre
and :post
.
;; -*- mode: lisp -*- fundamental-mode ;; Available everywhere (today (format-time-string "%Y-%m-%d")) prog-mode (fixme (if (derived-mode-p 'emacs-lisp-mode) ";; " comment-start) "FIXME ") (todo (if (derived-mode-p 'emacs-lisp-mode) ";; " comment-start) "TODO ") (bug (if (derived-mode-p 'emacs-lisp-mode) ";; " comment-start) "BUG ") (hack (if (derived-mode-p 'emacs-lisp-mode) ";; " comment-start) "HACK ") latex-mode (begin "\\begin{" (s env) "}" > n> r> "\\end{" (s env) "}") (frac "\\frac{" p "}{" p "}") (enumerate "\\begin{enumerate}\n\\item " r> n> "\\end{enumerate}") (itemize "\\begin{itemize}\n\\item " r> n> "\\end{itemize}") lisp-mode emacs-lisp-mode ;; Specify multiple modes (lambda "(lambda (" p ")" n> r> ")") emacs-lisp-mode (lambda "(lambda (" p ")" n> r> ")") (var "(defvar " p "\n \"" p "\")") (const "(defconst " p "\n \"" p "\")") (custom "(defcustom " p "\n \"" p "\"" n> ":type '" p ")") (face "(defface " p " '((t :inherit " p "))\n \"" p "\")") (group "(defgroup " p " nil\n \"" p "\"" n> ":group '" p n> ":prefix \"" p "-\")") (macro "(defmacro " p " (" p ")\n \"" p "\"" n> r> ")") (fun "(defun " p " (" p ")\n \"" p "\"" n> r> ")") (let "(let (" p ")" n> r> ")") (star "(let* (" p ")" n> r> ")") (rec "(letrec (" p ")" n> r> ")") (command "(defun " p " (" p ")\n \"" p "\"" n> "(interactive)" n> r> ")") eshell-mode (for "for " (p "i") " in " p " { " p " }") (while "while { " p " } { " p " }") (until "until { " p " } { " p " }") (if "if { " p " } { " p " }") (if-else "if { " p " } { " p " } { " p " }") (unless "unless { " p " } { " p " }") (unless-else "unless { " p " } { " p " } { " p " }") text-mode (cut "--8<---------------cut here---------------start------------->8---" n r n "--8<---------------cut here---------------end--------------->8---" n) (asciibox "+-" (make-string (length str) ?-) "-+" n "| " (s str) " |" n "+-" (make-string (length str) ?-) "-+" n) (rot13 (p "plain text" text) n "----" n (rot13 text)) (calc (p "taylor(sin(x),x=0,3)" formula) n "----" n (format "%s" (calc-eval formula))) rst-mode (title (make-string (length title) ?=) n (p "Title: " title) n (make-string (length title) ?=) n) java-mode (class "public class " (p (file-name-base (or (buffer-file-name) (buffer-name)))) " {" n> r> n "}") c-mode :condition (re-search-backward "^\\w*$" (line-beginning-position) 'noerror) (inc "#include <" (p (concat (file-name-base (or (buffer-file-name) (buffer-name))) ".h")) ">") (incc "#include \"" (p (concat (file-name-base (or (buffer-file-name) (buffer-name))) ".h")) "\"") org-mode (title "#+title: " p n "#+author: Daniel Mendler" n "#+language: en" n n) (quote "#+begin_quote" n> r> n> "#+end_quote") (example "#+begin_example" n> r> n> "#+end_example") (center "#+begin_center" n> r> n> "#+end_center") (comment "#+begin_comment" n> r> n> "#+end_comment") (verse "#+begin_verse" n> r> n> "#+end_verse") (src "#+begin_src " p n> r> n> "#+end_src") (elisp "#+begin_src emacs-lisp" n> r> n "#+end_src" :post (progn (tempel-done) (org-edit-src-code)))
All the Tempo syntax elements are fully supported. The syntax elements are
described in detail in the docstring of tempo-define-template
in tempo.el. We
document the important ones here:
p
Inserts an unnamed placeholder field.n
Inserts a newline.>
Indents with indent-according-to-mode
.r
Inserts the current region.r>
The region, but indented.n>
Inserts a newline and indents.&
Insert newline if there is only whitespace between line start and point.%
Insert newline if there is only whitespace between point and line end.o
Like %
but leaves the point before newline.(s NAME)
Inserts a named field.(p PROMPT <NAME> <NONINS>)
Insert an optionally named field with a prompt.
The PROMPT
is displayed directly in the buffer as default value. If NOINSERT
is non-nil, no field is inserted. Then the minibuffer is used for prompting
and the value is bound to NAME
.(r PROMPT <NAME> <NOINSERT>)
Insert region or act like (p ...)
.(r> PROMPT <NAME> <NOINSERT>)
Act like (r ...)
, but indent region.Furthermore Tempel supports syntax extensions:
(p FORM <NAME> <NONINS>)
Like p
described above, but FORM
is evaluated.(FORM ...)
Other Lisp forms are evaluated. Named fields are lexically bound.Use caution with templates which execute arbitrary code!
Tempel offers a flexible mechanism for providing the templates, which are
applicable to the current context. The variable tempel-template-sources
specifies a list of sources or a single source. A source can either be a
function, which should return a list of applicable templates, or the symbol of a
variable, which holds a list of templates, which apply to the current context.
By default, Tempel configures only the source tempel-file-templates
. You may
want to add global or local template variables to your user configuration:
(defvar my-global-templates '((example "Global example template")) "My global templates.") (defvar-local my-local-templates nil "Buffer-local templates.") (add-to-list 'tempel-template-sources 'my-global-templates) (add-to-list 'tempel-template-sources 'my-local-templates)
Important templates can be bound to a key with the small utility macro
tempel-key
which accepts three arguments, a key, a template or name and
optionally a map.
(tempel-key "C-c t f" fun emacs-lisp-mode-map) (tempel-key "C-c t d" (format-time-string "%Y-%m-%d"))
Internally tempel-key
uses tempel-insert
to trigger the insertion. Depending on
the style of your user configuration you may want to write your own helper
macros, which allow you to conveniently bind templates via use-package, general
or similar keybinding packages.
There are plenty of alternative packages which provide abbreviation or snippet expansion.
Since this package is part of GNU ELPA contributions require a copyright assignment to the FSF.
tempel-0.2.tar.lz | 2022-Jan-11 | 20.1 KiB |