Auto-closing Django template tags in Emacs
I have written two previous articles about how I edit Django template files in Emacs and XEmacs.
Here is
How
I edit Django templates. And here
is More
on editing Django templates in XEmacs. Here today is another
little tip that can be used in conjunction with those two other
posts or independently.
Django templates involve a lot of punctuation. Between the angle
brackets and slashes of HTML and the curly braces and percent
signs of the Django template language, it's enough to make your
pinky fingers hurt just thinking about it. Therefore any little
trick to reduce some of this typing burden can be
helpful. Presented here is some Emacs Lisp code to provide
auto-closing of Django template tags. So even if you still have
to type things like curly-brace percent-sign space ifequal blah
blah2 percent-sign close-curly-brace, you won't have to type the
{% endifequal %}. (Of course, if you're using
the abbrev tips I gave previously, you won't even need
to type the opening tag very often, but sometimes you still do.)
(more...)
More on editing Django templates in XEmacs
Previously, I wrote about how I set up syntax-highlighting for
Django template files using MMM (multiple major modes) in
XEmacs. This entry builds on the previous one, so if you haven't
read that one, I suggest doing so now.
This post addresses the age old question, "If (X)Emacs is so
good, why can't it do the typing for me?" Well, the answer, of
course, is it can. You just have to write a few lines of Lisp
first. Or you can copy and paste the Lisp code off of sites like
this one. ;)
Introducing dynamic abbreviations
Pressing M-/, (that is, holding down the Alt key and
pressing the forward slash key), runs the command
dabbrev-expand, which tries to finish whatever word you
are in the middle of typing. For example, if as I'm typing this
entry, I type the letter d, then press M-/,
"dabbrev-expand" shows up, because that was the last word I
typed that started with d. If I then type M-/
again, "dabbrev-expand" changes to "down". If I keep pressing
it, the word cycles through different guesses for what I'm going
for. If you've ever used VisualStudio, you may see some
similarities to the Intellisense feature. I find I almost never
type a whole word anymore, I've become so dependent on dynamic
completion.
Templates and more abbreviations
dabbrev-expand is useful to avoid having to type long
words over and over, but there are also longer patterns that
seem to need typing frequently. Thats where the tempo
package comes in handy, especially when combined with the
abbrev library. Here is some documentation for
tempo. Here is some documentation for abbrev.
Essentially, tempo allows you to specify "templates," or
blocks of standard text that can be parameterized as they are
filled in: the same concept as Django templates, but meant for
interactive use. From now on, I will say "tempo template" or
"Django template" to avoid confusing the two types.
abbrev lets you define your own pre-set abbreviations,
which can be filled in automatically as you type or upon
request. This can be combined with tempo to do some
pretty powerful stuff very easily.
I have a few Django-specific tempo templates and abbrevs set up
in my XEmacs initialization file. For example, as soon as I type
"{% block" followed by a space, the entire framework of a Django
template block is filled in for me. I've also added a special
menu to the menu bar for tempo templates I use frequently.
HTML and templates can be very repetitive, so I've found this
saves me a lot of typing.
My initialization file
Here is the subset of my ~/.xemacs/init.el file. This includes
the mmm-mode stuff I discussed last time, as well as the
tempo templates and abbrevs. I haven't put a tempo template for
every possible Django template tag, just the ones I use most
frequently. I'll leave further extension as an exercise for the
reader.
Also, some people don't like the expansion to happen
automatically as they are typing. To turn this off, comment out
the line that says "(setq abbrev-mode t)" by putting a semicolon
() in front of it. Then you can
manually expand tempo templates by pressing C-\
(control-backslash).
Once again, this is known to work in XEmacs version 21.4 on
Windows XP. It will most likely work in other versions of
XEmacs, and possibly in GNU Emacs.
(autoload 'css-mode "css-mode" "Mode for editing CSS files" t)
(add-to-list 'auto-mode-alist '("\\.css\\'" . css-mode))
(setq cssm-indent-function #'cssm-c-style-indenter)
(setq cssm-indent-level '2)
(add-to-list 'auto-mode-alist '("\\.tmpl\\'" . hm--html-mode))
(require 'mmm-vars)
(require 'mmm-mode)
(require 'mmm-sample)
(setq mmm-global-mode 'maybe)
(mmm-add-classes
'((my-django-expr
:submode python-mode
:face mmm-declaration-submode-face
:front "{%"
:back "%}"
:include-front t
:include-back t)))
(mmm-add-classes
'((my-django-var
:submode python
:face mmm-output-submode-face
:front "{{"
:back "}}"
:include-front t
:include-back t)))
(mmm-add-mode-ext-class nil "\\.tmpl\\'" 'embedded-css)
(mmm-add-mode-ext-class nil "\\.tmpl\\'" 'my-django-var)
(mmm-add-mode-ext-class nil "\\.tmpl\\'" 'my-django-expr)
(mmm-add-mode-ext-class nil "\\.tmpl\\'" 'html-js)
(setq mmm-submode-decoration-level 2)
(set-face-background 'mmm-code-submode-face "#EEEEFF")
(global-set-key "\C-\\" 'tempo-complete-tag)
(require 'adapt) (require 'hm--html-mode) (require 'tempo)
(tempo-define-template "span"
'("<span class=\"" (p "Class? ") "\">" r "</span>" >)
"<span "
"Insert a template for a span with a class")
(define-abbrev hm--html-mode-abbrev-table
"<span" "" 'tempo-template-span)
(tempo-define-template "script"
'(> "<script type=\"text/javascript\">" r "</script>" > %)
"<script "
"")
(define-abbrev hm--html-mode-abbrev-table
"<script" "" 'tempo-template-script)
(tempo-define-template "block"
'("{% block " (p "Block name? ") " %}" p "{% endblock %}")
"{% block "
"")
(define-abbrev hm--html-mode-abbrev-table
"{% block" "" 'tempo-template-block)
(tempo-define-template "django-if"
'("{% if " (p "Conditional? ") " %}" p "{% endif %}")
"{% if "
"")
(define-abbrev hm--html-mode-abbrev-table
"{% if" "" 'tempo-template-django-if)
(tempo-define-template "django-for"
'("{% for "
(p "Variable? ") " in "
(p "List? ") " %}" p
"{% endfor %}")
"{% for "
"")
(define-abbrev hm--html-mode-abbrev-table
"{% for" "" 'tempo-template-django-for)
(defun add-templates-menu ()
(interactive)
(add-submenu nil '("Tem%_plates"
["{%% %_block %%}" tempo-template-block]
["{%% %_if %%}" tempo-template-django-if]
["{%% %_for %%}" tempo-template-django-for]
["<%_span>" tempo-template-span]
["<s%_cript>" tempo-template-script])))
(add-hook 'hm--html-mode-hook
(lambda ()
(setq abbrev-mode t)
(add-templates-menu)))
How I edit Django templates
NOTE: This post is pretty old, and I no longer use mmm-mode for Django template editing.
This post and this post are still accurate, though.
Every programmer has their favorite editor and mode of
working. Some people have more than one. For example, I use
Microsoft Visual Studio when editing .NET code, DrScheme for editing
Scheme code, and XEmacs for pretty much everything else.
This post is about how I use XEmacs for editing Django template
files, in the hopes that others may find this useful.
The "Other" One True Editor
It's rare that people will get passionate about something as
pedestrian as a way of editing plain text, but the brief history
of the internet is awash with flamewars and heated discussions
with titles like "Emacs vs. XEmacs," "Emacs vs vi," and so
on. I'm not about to go into the relative merits, but the fact
that certain editors pop up time and time again in these debates
must mean there is something special about them.
Early on in my college education I started using Emacs because
it was all that was available on the school's servers. (Well,
that or vi, but vi was and still is black-magic to me.) I got
over the learning curve, and now I'm hooked.
At some point, I switched from Emacs to XEmacs, for reasons I
can't remember. At the time, it had some feature which was to me
essential. That reason no longer applies, but neither have I had
a reason to switch back. These tips may apply even if you use
GNU Emacs, but they've only been tested in XEmacs.
My current setup is Xemacs 21.4 (patch 20), running on Windows
XP. *gasp!* Yes, I use Windows for Django development. Shocking,
I know. I have my reasons. In any case, these tips should work
equally well with XEmacs on other platforms.
Multiple Major Modes
I won't include a full XEmacs tutorial here, since there is
already plenty of info on the web about it. The key point
is that there is a "major mode" for each programming
environment. There is a Python mode, a Java mode, and so
on. Django templates tend to combine more than one language in a
file, so that's when the mmm library comes in handy. It
stands for "multiple major modes," and it turns out to be just
exactly what we need. We can have html-mode for the HTML parts,
JavaScript-mode for the JavaScript parts, css-mode for embedded
CSS, and python-mode for the Django template filters and tags.
Which HTML mode?
As often happens in the world of Free Software, there are
several options to choose from when setting up HTML editing in
XEmacs.
- html-mode. This has the fullest support for HTML
parsing and validation. The problem is, when dealing with
templates, the HTMl will often not validate, so all kinds of
error messages show up.
-
sgml-mode. This is a more general mode for SGML (of
which XML and HTML 4 are subsets).
-
xml-mode. SGML mode specialized for XML.
-
hm--html-mode. I have no idea what the HM stands for,
but this is a lightweight HTML mode, with basic syntax
highlighting and indentation.
I use html-mode for full-fledged HTML documents, and
hm--html-mode for templates. So that XEMacs can tell the difference,
I use the suffix ".tmpl" for template files.
One problem
I did run into some problems getting mmm-mode to work with
XEmacs. It turns out the version of mmm-mode that is distributed
with the XEmacs package system is ancient - from 2001. I had to
download the newer version of mmm-mode and unzip it into my site-packages directory.
How it looks
Here's a screenshot of me editing an example Django template
(borrowed from my beta-registration Django app). I've chosen
fairly bright colors to make the syntax highlighting more
obvious, but that's all customizable. Notice how the Django tags
and variables are easy to find in the file. (Click on the image
for a larger size.)
My initialization file
Here is the subset of my ~/.xemacs/init.el file dealing with
setting up mmm-mode for XEmacs. I hope someone finds this
useful. Let me know if you do, or if you encounter problems.
(autoload 'css-mode "css-mode" "Mode for editing CSS files" t)
(add-to-list 'auto-mode-alist '("\\.css\\'" . css-mode))
(setq cssm-indent-function #'cssm-c-style-indenter)
(setq cssm-indent-level '2)
(add-to-list 'auto-mode-alist '("\\.tmpl\\'" . hm--html-mode))
(require 'mmm-vars)
(require 'mmm-mode)
(require 'mmm-sample)
(setq mmm-global-mode 'maybe)
(mmm-add-classes
'((my-django-expr
:submode python-mode
:face mmm-declaration-submode-face
:front "{%"
:back "%}"
:include-front t
:include-back t)))
(mmm-add-classes
'((my-django-var
:submode python
:face mmm-output-submode-face
:front "{{"
:back "}}"
:include-front t
:include-back t)))
(mmm-add-mode-ext-class nil "\\.tmpl\\'" 'embedded-css)
(mmm-add-mode-ext-class nil "\\.tmpl\\'" 'my-django-var)
(mmm-add-mode-ext-class nil "\\.tmpl\\'" 'my-django-expr)
(mmm-add-mode-ext-class nil "\\.tmpl\\'" 'html-js)
(setq mmm-submode-decoration-level 2)
(set-face-background 'mmm-code-submode-face "#EEEEFF")