• Stars
    star
    464
  • Rank 94,450 (Top 2 %)
  • Language
    Emacs Lisp
  • License
    GNU General Publi...
  • Created almost 3 years ago
  • Updated 9 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

🏛️ TempEl - Simple templates for Emacs

TempEl - Simple templates for Emacs

GNU Emacs GNU ELPA GNU-devel ELPA MELPA MELPA Stable

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 Tempel, a new implementation of Tempo with inline expansion and integration with recent Emacs facilities. Tempel takes advantage of the standard completion-at-point-functions mechanism which is used by Emacs for in-buffer completion.

Template expansion

Tempel comes with three commands for template expansion:

  • 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. The Capf returns a list of templates names which are presented by the completion UI for selection.
  • 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. This Capf returns only the single exactly matching template name, such that no selection in the completion UI is possible.
  • tempel-insert selects a template by name via completing-read and insert it into the current buffer.

For the 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. As soon as you move before (behind) the first (last) field, the fields are finalized. The key bindings are defined in the tempel-map keymap. I recommend that you inspect the tempel-map and look at the provided key bindings. You can customize the key bindings there.

Configuration

The package is available on GNU ELPA and MELPA and can be installed with package-install. The following example configuration relies on use-package. For some ready-made templates check out the package tempel-collection. The collection is not comprehensive yet, but will certainly grow thanks to contributions.

;; Configure Tempel
(use-package tempel
  ;; Require trigger prefix before template name when completing.
  ;; :custom
  ;; (tempel-trigger-prefix "<")

  :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 you
    ;; should also configure `tempel-trigger-prefix', such that Tempel
    ;; does not 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 'conf-mode-hook 'tempel-setup-capf)
  (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)
  ;; (global-tempel-abbrev-mode)
)

;; Optional: Add tempel-collection.
;; The package is young and doesn't have comprehensive coverage.
(use-package tempel-collection)

;; Optional: Use the Corfu completion UI
(use-package corfu
  :init
  (global-corfu-mode))

Template file format

The templates are defined in a Lisp data file configured by tempel-path. Lisp data files are files containing Lisp s-expressions (see lisp-data-mode). By default the file ~/.config/emacs/templates is used. The templates are grouped by major mode with an optional :when 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.

In addition, each template may specify a :pre and/or :post key with a FORM that is evaluated before the template is expanded or after it is finalized, respectively. The :post form is evaluated in the lexical scope of the template, which means that it can access the template’s named fields.

;; ~/.config/emacs/templates

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

(abstract "\\begin{abstract}\n" r> n> "\\end{abstract}")
(align "\\begin{align}\n" r> n> "\\end{align}")
(alignn "\\begin{align*}\n" r> n> "\\end{align*}")
(gather "\\begin{gather}\n" r> n> "\\end{gather}")
(gatherr "\\begin{gather*}\n" r> n> "\\end{gather*}")
(appendix "\\begin{appendix}\n" r> n> "\\end{appendix}")
(begin "\\begin{" (s env) "}" r> n> "\\end{" (s env) "}")
(center "\\begin{center}\n" r> n> "\\end{center}")
(displaymath "\\begin{displaymath}\n" r> n> "\\end{displaymath}")
(document "\\begin{document}\n" r> n> "\\end{document}")
(enumerate "\\begin{enumerate}\n\\item " r> n> "\\end{enumerate}")
(equation "\\begin{equation}" r> n> "\\end{equation}")
(flushleft "\\begin{flushleft}" r> n> "\\end{flushleft}")
(flushright "\\begin{flushright}" r> n> "\\end{flushright}")
(frac "\\frac{" p "}{" q "}")
(fussypar "\\begin{fussypar}" r> n> "\\end{fussypar}")
(itemize "\\begin{itemize}\n\\item " r> n> "\\end{itemize}")
(letter "\\begin{letter}\n" r> n> "\\end{letter}")
(math "\\begin{math}\n" r> n> "\\end{math}")
(minipage "\\begin{minipage}[t]{0.5\linewidth}\n" r> n> "\\end{minipage}")
(quotation "\\begin{quotation}\n" r> n> "\\end{quotation}")
(quote "\\begin{quote}\n" r> n> "\\end{quote}")
(sloppypar "\\begin{sloppypar}\n" r> n> "\\end{sloppypar}")
(theindex "\\begin{theindex}\n" r> n> "\\end{theindex}")
(trivlist "\\begin{trivlist}\n" r> n> "\\end{trivlist}")
(verbatim "\\begin{verbatim}\n" r> n> "\\end{verbatim}")
(verbatimm "\\begin{verbatim*}\n" r> n> "\\end{verbatim*}")

texinfo-mode

(defmac "@defmac " p n> r> "@end defmac")
(defun "@defun " p n> r> "@end defun")
(defvar "@defvar " p n> r> "@end defvar")
(example "@example " p n> r> "@end example")
(lisp "@lisp " p n> r> "@end lisp")
(bullet "@itemize @bullet{}" n> r> "@end itemize")
(code "@code{" p "}")
(var "@var{" p "}")

lisp-mode emacs-lisp-mode ;; Specify multiple modes

(lambda "(lambda (" p ")" n> r> ")")

emacs-lisp-mode

(autoload ";;;###autoload")
(pt "(point)")
(var "(defvar " p "\n  \"" p "\")")
(local "(defvar-local " 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> ")")
(alias "(defalias '" p " '" p ")")
(fun "(defun " p " (" p ")\n  \"" p "\"" n> r> ")")
(iflet "(if-let (" p ")" n> r> ")")
(whenlet "(when-let (" p ")" n> r> ")")
(whilelet "(while-let (" p ")" n> r> ")")
(andlet "(and-let* (" p ")" n> r> ")")
(cond "(cond" n "(" q "))" >)
(pcase "(pcase " (p "scrutinee") n "(" q "))" >)
(let "(let (" p ")" n> r> ")")
(lett "(let* (" p ")" n> r> ")")
(pcaselet "(pcase-let (" p ")" n> r> ")")
(pcaselett "(pcase-let* (" p ")" n> r> ")")
(rec "(letrec (" p ")" n> r> ")")
(dotimes "(dotimes (" p ")" n> r> ")")
(dolist "(dolist (" p ")" n> r> ")")
(loop "(cl-loop for " p " in " p " do" n> r> ")")
(command "(defun " p " (" p ")\n  \"" p "\"" n> "(interactive" p ")" n> r> ")")
(advice "(defun " (p "adv" name) " (&rest app)" n> p n> "(apply app))" n>
        "(advice-add #'" (p "fun") " " (p ":around") " #'" (s name) ")")
(header ";;; " (file-name-nondirectory (or (buffer-file-name) (buffer-name)))
        " -- " p " -*- lexical-binding: t -*-" n
        ";;; Commentary:" n ";;; Code:" n n)
(provide "(provide '" (file-name-base (or (buffer-file-name) (buffer-name))) ")" n
         ";;; " (file-name-nondirectory (or (buffer-file-name) (buffer-name)))
         " ends here" n)

eshell-mode

(for "for " (p "i") " in " p " { " q " }")
(while "while { " p " } { " q " }")
(until "until { " p " } { " q " }")
(if "if { " p " } { " q " }")
(ife "if { " p " } { " p " } { " q " }")
(unl "unless { " p " } { " q " }")
(unle "unless { " p " } { " p " } { " q " }")

text-mode

(box "┌─" (make-string (length str) ?─) "─┐" n
     "" (s str)                       "" n
     "└─" (make-string (length str) ?─) "─┘" n)
(abox "+-" (make-string (length str) ?-) "-+" n
      "| " (s str)                       " |" n
      "+-" (make-string (length str) ?-) "-+" n)
(cut "--8<---------------cut here---------------start------------->8---" n r n
     "--8<---------------cut here---------------end--------------->8---" 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 :when (re-search-backward "^\\S-*$" (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

(caption "#+caption: ")
(drawer ":" p ":" n r ":end:")
(begin "#+begin_" (s name) n> r> n "#+end_" name)
(quote "#+begin_quote" n> r> n "#+end_quote")
(sidenote "#+begin_sidenote" n> r> n "#+end_sidenote")
(marginnote "#+begin_marginnote" n> r> n "#+end_marginnote")
(example "#+begin_example" n> r> n "#+end_example")
(center "#+begin_center" n> r> n "#+end_center")
(ascii "#+begin_export ascii" n> r> n "#+end_export")
(html "#+begin_export html" n> r> n "#+end_export")
(latex "#+begin_export latex" n> r> n "#+end_export")
(comment "#+begin_comment" n> r> n "#+end_comment")
(verse "#+begin_verse" n> r> n "#+end_verse")
(src "#+begin_src " q n> r> n "#+end_src")
(gnuplot "#+begin_src gnuplot :var data=" (p "table") " :file " (p "plot.png") n> r> n "#+end_src" :post (org-edit-src-code))
(elisp "#+begin_src emacs-lisp" n> r> n "#+end_src" :post (org-edit-src-code))
(inlsrc "src_" p "{" q "}")
(title "#+title: " p n "#+author: Daniel Mendler" n "#+language: en")

;; Local Variables:
;; mode: lisp-data
;; outline-regexp: "[a-z]"
;; End:

Template syntax

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:

  • “string” Inserts a string literal.
  • p Inserts an unnamed placeholder field.
  • n Inserts a newline.
  • > Indents with indent-according-to-mode.
  • r Inserts the current region. If no region is active, quits the containing template when jumped to.
  • r> Acts like r, but indent region.
  • n> Inserts a newline and indents.
  • & Insert newline unless there is only whitespace between line start and point.
  • % Insert newline unless 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> <NOINSERT>) 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> <NOINSERT>) Like p described above, but FORM is evaluated.
  • (FORM ...) Other Lisp forms are evaluated. Named fields are lexically bound.
  • q Quits the containing template when jumped to.

Use caution with templates which execute arbitrary code!

Defining custom elements

Tempel supports custom user elements via the configuration variable tempel-user-elements. As a demonstration we add the element (i template) to include templates by name in another template.

(defun tempel-include (elt)
  (when (eq (car-safe elt) 'i)
    (if-let (template (alist-get (cadr elt) (tempel--templates)))
        (cons 'l template)
      (message "Template %s not found" (cadr elt))
      nil)))
(add-to-list 'tempel-user-elements #'tempel-include)

The following example templates uses the newly defined include element.

(header ";;; " (or (buffer-file-name) (buffer-name)) " -- " p
        " -*- lexical-binding: t -*-" n n)
(provide "(provide '" (file-name-base (or (buffer-file-name) (buffer-name))) ")" n
         ";;; " (file-name-nondirectory (or (buffer-file-name) (buffer-name))) " ends here" n)
(package (i header) r n n (i provide))

Adding template sources

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-path-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)

Hooking into the Abbrev mechanism

Tempel can hook into Abbrev by enabling the tempel-abbrev-mode in a buffer or by enabling the global-tempel-abbrev-mode. Then the Tempel templates will be available via expand-abbrev which is usually bound to ~C-x ‘~.

Binding important templates to a key

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.

Alternatives

There are plenty of alternative packages which provide abbreviation or snippet expansion. Try Tempel 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).

List of alternatives (built-in or separate packages):

  • abbrev.el: Abbreviation expansion, builtin
  • expand.el: Abbreviation expansion, builtin
  • skeleton.el: Lisp syntax for templates, builtin
  • tempo.el: Lisp syntax for templates, builtin
  • srecode.el: CEDET template manager and code generator, builtin
  • aas.el: Auto activating snippets
  • cdlatex.el: Fast LaTeX insertion
  • laas.el: Latex auto activating snippets
  • muban.el: Lightweight template expansion
  • placeholder.el: Treat buffers as templates
  • tempo-abbrev.el: Abbrev integration for Tempo
  • snippet.el: Original snippet mode, with inline expansion
  • tempo-snippets.el: Interface like snippet.el for Tempo
  • yasnippet.el: Template system inspired by Textmate snippets

Contributions

Since this package is part of GNU ELPA contributions require a copyright assignment to the FSF.

More Repositories

1

org-modern

🦄 Modern Org Style
Emacs Lisp
1,344
star
2

vertico

💫 vertico.el - VERTical Interactive COmpletion
Emacs Lisp
1,344
star
3

consult

🔍 consult.el - Consulting completing-read
Emacs Lisp
1,088
star
4

corfu

🏝️ corfu.el - COmpletion in Region FUnction
Emacs Lisp
1,000
star
5

marginalia

📜 marginalia.el - Marginalia in the minibuffer
Emacs Lisp
762
star
6

cape

🦸cape.el - Completion At Point Extensions
Emacs Lisp
533
star
7

osm

🌍 osm.el - OpenStreetMap viewer for Emacs
Emacs Lisp
496
star
8

jinx

🪄 Enchanted Spell Checker
Emacs Lisp
328
star
9

affe

🐒 affe.el - Asynchronous Fuzzy Finder for Emacs
Emacs Lisp
202
star
10

lmdb

Ruby bindings for the OpenLDAP's Lightning Memory-Mapped Database (LMDB)
C
105
star
11

goggles

goggles.el - Pulse modified region
Emacs Lisp
92
star
12

bibsync

BibSync is a tool to synchronize scientific papers and bibtex bibliography files
Ruby
60
star
13

paripari

Fast parser combinator library for Haskell with two strategies (Fast acceptor and slower reporter with decent error messages)
Haskell
56
star
14

hasklig-mode

hasklig-mode.el - Hasklig ligatures for emacs
Emacs Lisp
39
star
15

gridslides

LaTeX package to create free form slides with blocks placed on a grid
TeX
38
star
16

3delta

Host software especially suited for delta 3d printers
Tcl
36
star
17

pacgem

Gem installer for Arch Linux
Ruby
34
star
18

bookmark-view

bookmark-view.el - Use bookmarks to persist the current view
Emacs Lisp
34
star
19

osm2shp

Convert large OpenStreetMap files to shapefiles (Uses sqlite3 db as temporary storage)
C++
29
star
20

consult-flycheck

Consult integration for Flycheck
Emacs Lisp
28
star
21

chasm

Java Bytecode Assembler and Disassembler which uses S-expressions
Java
26
star
22

tab-bookmark

Persist Emacs Tabs as Bookmarks
Emacs Lisp
25
star
23

unit

Unit support for numbers
Ruby
24
star
24

mini-popup

Emacs Lisp
22
star
25

gitrb

Unmaintained: Simple git implementation in ruby similar to grit, based on git_store
Ruby
21
star
26

recursion-indicator

Recursion indicator for the mode-line
Emacs Lisp
20
star
27

henk

pure type system language - henk 2000 ported to megaparsec, pretty
Haskell
20
star
28

persist

Minimal binary serialization library with focus on performance
Haskell
17
star
29

rack-embed

Rack middleware which embeds small images as base64 data-url in css/html files if the browser supports it
Ruby
16
star
30

polyp

Emacs Lisp
12
star
31

writer-cps-mtl

Stricter drop in replacements for WriterT and RWST
Haskell
11
star
32

intro

Safe and minimal Haskell Prelude
Haskell
10
star
33

writer-cps-transformers

Compatibility package for old transformers: Stricter drop in replacements for WriterT and RWST
Haskell
10
star
34

wl-pprint-annotated

Wadler/Leijen pretty printer with annotations and API conforming to modern Haskell
Haskell
7
star
35

uchronia

uchronia.el - Rewrite the minibuffer history
Emacs Lisp
7
star
36

tasty-stats

Collect statistics of your Tasty testsuite
Haskell
7
star
37

tasty-auto

Deprecated: Auto discovery for the Tasty test framework, use tasty-discover instead
Haskell
6
star
38

quickcheck-special

Edge cases and special values for QuickCheck Arbitrary instances
Haskell
5
star
39

thinkpad-tools

Unmaintained: Some scripts used on my Thinkpad X60t
Python
5
star
40

typohero

TypoHero enhances your typography
Ruby
5
star
41

diascope

Unmaintained: S5: A Simple Standards-Based Slide Show System. Alternative implementation using jquery
JavaScript
5
star
42

colorful-monoids

colorful-monoids: Styled console text output using ANSI escape sequences
Haskell
4
star
43

9mount

Unmaintained fork of http://sqweek.dnsdojo.org/hg/9mount
C
4
star
44

omega

Haskell
4
star
45

distcc

distcc scripts
4
star
46

evaluator

Mathematical expression evaluator for infix notation. Supports variables and functions.
Ruby
3
star
47

xosdbar

show osd on X11, similar to osd_cat but with update support
C
3
star
48

wl-pprint-console

Wadler/Leijen pretty printer with annotations and support for colorful console output
Haskell
3
star
49

andromeda.hs

This is a direct port of Andrej Bauer's dependent type theory implementation from OCaml to Haskell
Haskell
3
star
50

mapgen

XCSoar Map Generator
Python
3
star
51

vle

Verilog Experiments
Verilog
3
star
52

console-style

Styled console text output using ANSI escape sequences
Haskell
3
star
53

analyzer

Some tools for physics calculations
C++
3
star
54

doxygen2adoc

Doxygen import for Asciidoc using XSLT and the Doxygen XML output
XSLT
3
star
55

ihs

Interpolated Haskell
LLVM
3
star
56

bigint-unboxed

JavaScript big integers with support for ES6 BigInt and unboxed small integers
JavaScript
2
star
57

special-values

Haskell
2
star
58

hashtable

Hashtable in C with open addressing and specialization via macros
C
2
star
59

i3-config

qubes i3 configuration
Shell
2
star
60

TaskList

Mediawiki plugin which implements a task tracker
PHP
2
star
61

implant

Linux from scratch package manager using symlinks - similar to GoboLinux (2008)
Shell
2
star
62

hexns

Nameserver for IPv6 which resolves Hexspeak subdomains
C
2
star
63

emacs-theme

Emacs Lisp
2
star
64

arch-packages

My arch pkbuilds
Shell
2
star
65

temple-mustache

Temple-based implementation of Mustache (Just a finger exercises to test temple)
Ruby
2
star
66

encode-string

String encoding and decoding in Haskell
Haskell
2
star
67

writer-cps-monads-tf

Stricter drop in replacements for WriterT and RWST
Haskell
2
star
68

slim-examples

http://slim-lang.com/
Ruby
2
star
69

imaginator

Unmaintained: Image generator for LaTex/graphviz source
Ruby
1
star
70

safe-convert

Safe type conversions in Haskell
Haskell
1
star
71

echoxul

echo2 java web app + xul (2006)
Java
1
star
72

persist-state

Haskell
1
star
73

strict-base

Strict versions of some standard Haskell data types
Haskell
1
star
74

writer-cps-full

Stricter drop in replacements for WriterT and RWST
Haskell
1
star
75

writer-cps-exceptions

Control.Monad.Catch instances for CPS WriterT and RWST monad transformers
Haskell
1
star
76

grouptsp

Travelling salesman (2004)
Java
1
star
77

pasty

easy to deploy, single file, under 50 line, command line pastebin
PHP
1
star
78

arduino-pi-gefrickel

Arduino/Rasberry Pi Gefrickel - Arduino/Rasberry Pi Fiddling
JavaScript
1
star
79

intro-prelude

Reexport Intro as Prelude
Haskell
1
star
80

os-experiment

simple i386 os with console and multithreading (2005)
C
1
star