I have a bibtex file, and would like to have indentation as follows.

       AUTHOR = {John W. Moon}, 
        TITLE = {Counting labelled trees},
         YEAR = 1970,
       SERIES = {Canadian Mathematical Monographs},
       NUMBER = 1,
    PUBLISHER = {Canadian Mathematical Congress}

I am thinking of creating a indent-bibtex-entry command, but I am stuck in steps 4 and 5.

  1. Save current position, jump to the previous ocurrence of the regular expression ^[ \t]*@, then to the next { and then to the next ,.
  2. While next nonblank is not }
  3. Capture the next field with the regular expression [ \t\n]*\([^ \t=]+\)=.
  4. Replace by \n \1 = where the number of spaces must make the total length of this expression equal to, say, 15.
  5. Capture the entry's content (see caveats below), removing line-breaks and spaces before and after.
  6. Goto 2.
  7. Restore cursor's position.


The content of an entry is somewhat tex-aware. The entries are text between quotes "like this" or between matching braces {like this}. However, you may have braces (as long as they're balanced) inside, and escaped quotes as well {na\"ive {example}}.

Simplier, but also nice

If that is too difficult, an alternative is to define a command indent-field, assuming that the line contains a single field. The problem is similar to the previous step 4 (but here it is step 3).

  1. Save current position, jump to the beginning of the line.
  2. Capture the next field with the regular expression [ \t\n]*\([^ \t=]+\)=[ \t\n*]*.
  3. Replace by \n \1 = where the number of spaces must make the total length of this expression equal to, say, 15.
  4. Restore cursor's position.

Any suggestions? I am still working on the code for the items I "know" how to do. This is my first time with elisp.

1 Answer 1


Here's a possible solution for you. I broke up the code so you might be able to learn a little elisp at the same time...

(defun indent-me ()
  "Indent a bibtext entry.
This assumes the entry starts with a non-white in column 0, and
ends with a } on a line by itself.  Invoke the function anywhere
inside the entry."
    ;; go to col 0 and search upwards for the start of the entry
    (goto-char (line-beginning-position))
    (while (not (looking-at "^@book"))
      (forward-line -1))
    ;; now looking at the start of entry, eg. @book
    (forward-line 1)
    (while (not (looking-at "^}"))
      (forward-line 1))

(defun indent-one-line ()
  "Indent one line of the entry."
  (let (tokens tag newline)
    (setq tokens (split-string (line-contents)))
    (setq tag (car tokens))
    (setq tag (string-pad-left tag 15))
    (setq newline (cdr tokens))
    (setq newline (push tag newline))
    (setq newline (mapconcat 'concat newline " "))
    (goto-char (line-beginning-position))
    (insert newline)

(defun line-contents ()
  "Return the contents of the current buffer line."
   (line-beginning-position) (line-end-position)))

(defun string-pad-left (string length)
  "Return STRING padded left to LENGTH."
  (let (result)
    (while (> length (length string))
      (setq string (concat " " string)))
    (setq result string)
  • This is perfect! I managed to build a complete solution. Small tweak: I managed to remove the space-padding loop, using make-string instead (manual)
    – Tássio
    Nov 6, 2016 at 14:28
  • Where exactly this code should be placed?
    – Viesturs
    Jul 13, 2017 at 14:45
  • @Viesturs, you could have this on your ~/.emacs file, for instance, or save it in a file and then load it from your ~/.emacs.
    – Tássio
    Jul 13, 2017 at 15:07
  • I copied it into my ~/.emacs.d/init.el file but when I reopen emacs I see no changes in my bibtex file. And will this code not interfere with cc, tex and other files?
    – Viesturs
    Jul 13, 2017 at 15:15
  • @Viesturs, once this code is loaded, what you have is 1) open your bibtex file; 2) position your cursor inside of the entry you want to indent, 3) M-x indent-me to indent that entry.
    – Tássio
    Aug 4, 2017 at 14:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.