Using XEmacs to edit LaTeX files

The power of XEmacs becomes especially obvious when editing LaTeX files. LaTeX is a wonderful typesetting system, but sometimes it can require too much typing to do the job. Here I will explain how to make XEmacs do much of the typing for you, by using abbreviations. And when I say abbreviations, I don't mean creating a preamble in the LaTeX document which will define short names for long commands. I mean one can actually teach XEmacs that whenever you type for instance \rla and hit the space key, then you actually mean \rightleftarrow, and have XEmacs replace the former with the latter.

That enough would already be great. But XEmacs can do more. What if one could type in \eq, hit the space key, and get as a result the equation environment,

\begin{equation}\label{}

\end{equation}
and in addition the cursor would go right where it is supposed to be, in between the curly braces of \label{}? That is possible. We will need to write a small emacs lisp function to insert the above and then move the cursor back as many positions as needed to place it where it should be. Then we will make the \eq abbreviation call this function. Below I explain how to implement all of this.

First, let us create a file where we will store all LaTeX-related preferences and functions for XEmacs. And then we need to tell XEmacs to load that file every time we edit a LaTeX file. We can for instance name that file my-latex.el and store it in the directory .xemacs. Then, paste the following in init.el

; associate the files with the .tex extension with the LaTeX mode
(setq auto-mode-alist (append '(("\\.tex$"  . LaTeX-mode)) auto-mode-alist))

; load the LaTeX mode
(require 'tex-site)

; Tell XEmacs to load `my-latex.el' when opening LaTeX files
(add-hook 'LaTeX-mode-hook
  '(lambda()
     (load-file "~/.xemacs/my-latex.el")  ; load these LaTeX preferences
     ))

Next, we will tell XEmacs to enable the abbreviations mode. The abbreviations themselves will be stored in a file called my-abbreviations.el in the directory .xemacs. To do this, enter the following in init.el.

(setq-default abbrev-mode t)                            ; enable abbreviations
(setq save-abbrevs t)                                   ; save abbreviations upon exiting xemacs
(setq abbrev-file-name "~/.xemacs/my-abbreviations.el") ; the file storing the abbreviations
(if (file-readable-p abbrev-file-name)                  ; read the abbreviations every
  (read-abbrev-file abbrev-file-name)                   ; time xemacs is started
  )

Here is how the file my-abbreviations.el can look like:

; Define the table of abbreviations in latex-mode.  Note that we have
; to use '\\' to stand for backslash, since, just like in LaTeX, in
; emacs lisp the backslash character is special.  It would have been
; logical for this table to be called latex-mode-abbrev-table, but for
; some reason that does not work.
(define-abbrev-table 'text-mode-abbrev-table '(
  ("\\a" "\\alpha" nil 0)                  ; define \a to be an abbreviation for \alpha    
  ("\\b" "\\beta" nil 0)                   ; and \b for \beta 
  ("\\lra" "\\leftrightarrow" nil 0)       ; and \rla for \leftrightarrow
  ; Other abbreviations go here. Note the closing parentheses below.
 )
)

Let us look at the syntax employed in the abbreviation table. It is clear that in every row of the table the first component defines the abbreviation, and that the second component defines the abbreviation expansion, that is, what we want the abbreviation replaced with. In the third position there is a nil. Actually, there we can put the name of an emacs lisp function, and then the abbreviation will call that function. And this is where all the power of abbreviations comes from. By calling a function it is possible to make much more complicated operations than just the simple replacement of an abbreviation with its expansion. That will be illustrated below. So far, the third component is nil, which does nothing. Finally the fourth component is an integer number. There XEmacs records how many times an abbreviation has been used so far. That is to say, that place keeps the statistics of abbreviations usage.

Now let us see how to define an abbreviation which upon being expanded will call a function. Consider the following example. We want the abbreviation \ci to get expanded into \cite{} and the cursor to be put inside the curly brackets. That can be done with the following line in the abbreviation table:

  ("\\ci" "\\cite{}" backward-char 0)

After inserting \cite{}, the standard emacs lisp function backward-char is called, which moves the cursor back one character and thus positioning it inside the curly brackets.

How about adding an entry in the abbreviation table to make \fr expanded into \frac{}{} and put the cursor inside the first pair of curly brackets? Well, we could repeat the above, but move back the cursor three characters instead of one. Then we need to call backward-char with the argument 3 (move back 3 chars). Unfortunately, the syntax of the abbreviation table does not allow for that, so we are out of luck.

But never give up. Let us make our own function to move the cursor back 3 characters. That could not be easier.

; define an emacs lisp function to move the cursor back three characters
(defun move-back-three-chars ()  ; the function is called 'move-back-three-chars'
  (interactive)                  ; all emacs lisp functions must have this line
  (backward-char 3)              ; this function will do nothing but move the cursor 3 chars 
  )

Along the way we learned the syntax of an emacs lisp function. Now the abbreviation for the fraction would be defined as follows:

  ("\\fr" "\\frac{}{}" move-back-three-chars 0)

This could be done in a more elegant way. Define a function which will both do the insertion of \frac{}{} and move the cursor inside the first pair of curly brackets.

; an emacs lisp function to insert '\frac{}{}' and then move the cursor back three characters
(defun my-latex-fraction ()
  (interactive)
  (insert "\\frac{}{}")    ; insert a piece of text
  (backward-char 3)        ; move the cursor back three characters 
  )

Let us save this function in my-latex.el, as we will call it when in LaTeX mode. Define the corresponding abbreviation in my-abbreviations.el:

  ("\\fr" "" my-latex-fraction 0)

Note the empty quotes here. That is because when \fr is expanded, at first nothing is inserted, and then the function my-latex-fraction is called which does all the work.

Also note an important fact. The abbreviations are stored in my-abbreviations.el. If a given abbreviation needs to call a function, and the function is user defined, then the body of that function must be stored somewhere outside my-abbreviations.el. If that abbreviation and the corresponding function is to be called in LaTeX mode, then most appropriate place for the function to be stored is in my-latex.el.

And here is the emacs lisp function necessary to insert the equation environment described at the beginning.

(defun my-latex-equation ()      ; a function to insert the equation environment
  (interactive)                  
  (insert "\\begin{equation}\\label{}\n") ; \n is the newline character
  (insert "  \n")
  (insert "\\end{equation}") 
  (previous-line 3)              ; move back to the line containing \begin... 
  (forward-char 24)              ; move inside the curly brackets
  )

The last issue is how to tell XEmacs to expand a given abbreviation. A solution is the following: every time the space key is hit, check if the recently typed word is an abbreviation. If it is, expand it, if it is not, just insert an empty space, which is the normal behavior we would expect from the space key. I wrote a function called smart-space to do this job. Here is the code for that (this needs to go in my-latex.el).

(defun smart-space () ; make the space key behave in a smarter way
  (interactive)
  (if (not (expand-abbrev)) ; test if the current word is an abbrev. If yes, expand it.
      (insert " ")          ; If not, insert a plain space
    )                       ; Now you know how to use 'if' in emacs lisp
  )
(local-set-key [(space)] 'smart-space) ; bind the 'smart-space' function to the 'space' key

Here is a my-latex.el file which contains the functions we defined above and some other ones also useful in LaTeX-mode. And here is the corresponding my-abbreviations.el file where the abbreviations (some calling functions from my-latex.el) are stored. Two reminders: first is that in order for all this to work one must not forget to insert into the file init.el the two pieces of emacs lisp code at the beginning of this page. Second, the files my-latex.el and my-abbreviations.el need to go in the .xemacs directory.

But the tricks XEmacs has to offer are not over yet. One can define abbreviations on the fly, by using the command define-mode-abbrev. Let us bind it to the shortcut Alt-a by inserting the following in my-latex.el

(local-set-key [(meta a)] 'define-mode-abbrev); define abbrevs on the fly with Alt-a

Now, when you are typing a LaTeX document and notice that you write \mathbb R way to often, just hit Alt-a, and set \r to be an abbreviation for it. When you exit XEmacs it will ask whether to insert this abbreviation in my-abbreviations.el for future use.

Lastly, XEmacs also has dynamic abbreviations. If you for instance have the word international in your document, then the second time you need it you can just type inter, then tell XEmacs to go and look up a completion for you and finish the typing. The command for that is dabbrev-expand, which can be bound for example to Alt-space by inserting the following in my-latex.el

(local-set-key [(meta space)] 'dabbrev-expand) ; expand dinamic abbreviations with Alt-space

Of course, when you hit Alt-space, XEmacs might choose to expand inter to internal, if that word is also in your document. But then one can just hit Control-z to undo this, type a couple more of letters in the word you want completed, and then try your luck again with a dynamic expansion. In my experience, this works well 95% of the time.


Back: XEmacs tips aoleg@math.umn.edu Created with XEmacs Valid XHTML 1.0! Updated: March 07, 2004