Emacs auto-insert
The Emacs manual has a section called Autoinserting Text in Empty Files which explains how to insert something akin to a template when opening a file. I tried it recently and document my steps and understanding below. I primaily used the Emacs manual and a post from https://howardism.org/ titled Having Emacs Type for You.
First, I needed to turn on auto-insert-mode
:
(auto-insert-mode t)
Then the simplest thing I tried was to define the variable
auto-insert-alist
based on reading its documentation. (This
variable already exists in a default Emacs installation but more on
that later.)
This variable is a special list that takes either of two forms:
(CONDITION . ACTION) ; or ((CONDITION . DESCRIPTION) . ACTION)
The documentation describes these terms:
CONDITION
- (a) a regular expression that must match the new file's name, or (b) a symbol that must match the major mode for the new file
ACTION
- (a) an absolute filename, (b) a filename relative to
auto-insert-directory
, (c) a skeleton to insert, or (d) a function to call. DESCRIPTION
- is a string for filling
auto-insert-prompt
(optional)
In the next three sections I document ACTION
being based on an absolute file
path, a path relative to a directory, and a skeleton.
Inserting a file specified with an absolute path
The text below is the contents of a template in the form of a file
called auto-insert.R
which is stored in my home directory.
######################################## ## An R header from my home directory ## ########################################
Next is a very simple auto-insert-alist
dealing only with R files
that will insert this header when I create a new R file. The regular
expression is meant to match a filename that has a literal period
followed by either R
or r
followed by the end of the string. (I
believe $
denotes the end of a line in Emacs regular expression
syntax so is not used here.)
(setq auto-insert-alist '(("\\.[rR]\\'" . "~/auto-insert.R"))) ;; ^ absolute path
Now, when I open a new R file I am asked whether I want to perform auto-insertion. So that is working.
Setting auto-insert-query
to nil
means I won't be prompted before
inserting the template:
(setq auto-insert-query nil)
Inserting a file specified relative to auto-insert-directory
I can also put my various auto-insert files in a common directory and
tell Emacs about this directory. Here is my R template in
~/.emacs.d/auto-insert/auto-insert.R
:
########################################################### ## An R header from my ~/.emacs.d/auto-insert directory ### ###########################################################
I next needed to tell Emacs which directory I used. (I am using Linux
and the documentation for auto-insert-directory
says I need to have
the directory end in a slash so that it is explicitly an absolute path
to a directory.)
(setq auto-insert-directory "~/.emacs.d/auto-insert/") ;; ^^^ absolute path ^^^ (setq auto-insert-alist '(("\\.[rR]\\'" . "auto-insert.R"))) ;; relative to dir
Here is the same as above but with the DESCRIPTION
functionality.
(setq auto-insert-alist '((("\\.[rR]\\'" . "My auto-insert for R files") . "auto-insert.R")))
Insertion based on invoking a skeleton
The documentation says ACTION
can be an Emacs skeleton. Skeletons
have a special Skeleton syntax and are a wild mix of Emacs lisp,
constants, strings, magic, etc.
To understand a moderately complicated skeleton, here is the built-in
LaTeX mode element of auto-insert-alist
. (This is one element of a
much longer auto-insert-alist
.) The CONDITION
is not a filename
regular expression but a symbol that matches a major mode.
The logic is as follows:
- Prompt for document class options and handle none if applicable
- Prompt for document class
- Prompt for package options and handle none if applicable
- Prompt for package name
- Repeat prompting for package options and package name until done
- Add the begin and end document tags
(latex-mode ; I don't know why there is no `.' "options, RET: " ; first prompt for documentclass options ;; If anything entered at first prompt then add a closing char 93 ;; (`]') otherwise back up one space to delete the opening square ;; bracket and then type char 123 (`{') "\\documentclass[" str & 93 | -1 123 ;; this is the 2nd prompt which asks for and reads the document class (read-string "class: ") "}\n" ;; This is a subskeleton, a complete skeleton in a skeleton. ;; It prompts for package information and repeats until the ;; user has no knew packages to enter. ;; Third prompt. The `%s' is linked to `RET' I think ("package, %s: " "\\usepackage[" ;; read the package options (read-string "options, RET: ") ;; Similar to above different so confusing & 93 | -1 123 str "}\n") _ " \\begin{document} " _ " \\end{document}")
I wanted to do something similar and ended up with this skeleton.
(setq auto-insert-alist '((("\\.[rR]\\'" . "My Skeleton template for R files") nil ;; No prompt string functionality initially "## Who: " (progn user-full-name) "\n" ; grap user's name ;; Prompt for and enter a short description "## What: " (read-string "short description: ") "\n" ;; Add date like 'December 5, 2025' but if two spaces between ;; month and day of month zap one space "## When: Started " (replace-regexp-in-string " " " " (format-time-string "%B %e, %Y")) "\n" ;; Prompt for and enter a short purpose. If something is entered ;; add a char 10 (return). If nothing is entered back up 9 spaces ;; which zaps `## Why: ' "## Why: " (read-string "short purpose: ") & 10 | -9 ;; Prompt for and enter how the program works using the same logic "## How: " (read-string "how it works: ") & 10 | -9 ;; This is a one-line subskeleton for looping through packages to ;; add. Character 41 is close paren. The skeleton interpreter ;; handles both no packages and multiple packages. ("package: " "\nlibrary(" str & 41) "\n" _ ; Cursor here ;; Define the `compile-command' so it's "active" initially. ;; The single quote at the start of the expression tells auto-insert ;; to evaluate the expression and not insert anything '(setq compile-command (format "Rscript --no-save --no-restore \"%s\"" (file-name-nondirectory buffer-file-name))) ;; Specify file local variables at bottom of file, writing out ;; the compile command. "\n\n\n\n\n" "## For Emacs users:\n" "## Local Variables:\n" "## compile-command: \"Rscript --no-save --no-restore " (file-name-nondirectory buffer-file-name) " \"\n" "## End:\n")))
The above is working and it was probably faster for me to try to learn some Skeleton syntax than me writing an Emacs lisp function to do the same. But a skeleton is not easy to read and an Emacs lisp function may be a better route.
(To see the state of the art, try auto-insert
on an Emacs lisp
file.)
Adding functionality to the existing auto-insert-alist
The function define-auto-insert
allows one to add CONDITION
and
ACTION
pairs to an existing auto-insert-alist
. This means one can
keep using the built-in auto-insert-alist
which handles a number of
different files. Here is the signature and docstring for
define-auto-insert
.
(define-auto-insert CONDITION ACTION &optional AFTER)
Associate CONDITION with (additional) ACTION in ‘auto-insert-alist’. Optional AFTER means to insert action after all existing actions for CONDITION, or if CONDITION had no actions, after all other CONDITIONs.
Here is an example of using this approach. For brevity I don't use the skeleton
but instead specify a file in the auto-insert-directory
.
(define-auto-insert "\\.[rR]\\'" "template.R")
After evaluating the expression, this entry is at the front of the
auto-insert-alist
.
Conclusion
It was fun to learn the auto-insert
functionality. I spent a lot of
time learning and writing up what is in this document and I will need
to create hundreds of new R files before I start to save any time. But
with this file I should be able to make better, faster, stronger
skeletons for more file types.