Lisp with Vim

by Larry Clapp

Tuesday, 9 April 2002

For this inagural edition of the Lisp magazine at lisp-p.org, I'd like to mention some of the things a Vim user coding Lisp should know about. This article assumes some familiarity with Vim, but mentions some points anyway (like the "%" key, and how to get help). I should also mention that, while I'm not new to Vim or programming in general, I am new to Lisp. Caveat reader.

What is Vim?

From one of the help files:

Vim stands for Vi IMproved. It used to be Vi IMitation, but there are so many improvements that a name change was appropriate. Vim is a text editor which includes almost all the commands from the Unix program "Vi" and a lot of new ones. It is very useful for editing programs and other plain text.

You can find lots and lots of Vim information at www.vim.org.

A FAQ: Why not just use Emacs?

Because I'm busy enough learning Lisp without having to learn Emacs to boot. And Viper doesn't emulate Vim, it emulates Vi, as near as I can tell from a cursory look. I know Vim, and I like Vim, and I don't know Emacs, and think that at least one other person in the world shares my prejudice.

To begin: How to get help from Vim:

:help <topic>

Examples:

:help %
:help help

If you want to know about indenting, for example, type ":help indent", but don't press enter, and press TAB; Vim will cycle through all the help topics it has that begin with or include the string "indent" (about 25 in my version).

Movement commands:

% find the matching parenthesis (or bracket or brace). See ":help %"
[( go to [count] previous unmatched "(". Go "up" one level of nesting, towards the beginning of the expression.
[) go to [count] previous unmatched ")". Like [(, but goes towards the end of the expression.

I've found 99[( useful for going to the beginning of a defun or macro. From there, you can just say y% to yank the whole form.

Suggestion: ":map [9 [(" and ":map ]0 [)" should save a few keystrokes, over time.

Suggestion: pick some keys you don't use (or don't need :) and map them to searches for ( and ). E.g.,

:map <C-F9> /(<cr>
:map <C-F10> /)<cr>

Not-so-minor technical details about the above two commands: Since my Vim doesn't seem to understand function keys with Ctrl pressed even though my xterm generates distinct codes for them, I had to first do this:

:map <esc>[20;5~ <C-F9>
:map <esc>[21;5~ <C-F10>

I entered the left-hand-side (lhs) of the first mapping by pressing Ctrl-V then Ctrl-F9.

Marking, yanking, deleting text:

NOTE to Emacs users, to avoid confusion & other flames: the vi & Emacs communities mean different things by "yank". In vi derivatives, "yank" means "take the indicated text and put it into an internal register", and "put" means "take the text out of the internal register and stick it back into the buffer".

Knowing about Vim's text object selection can save you some work. ":help text-objects" says:

This is a series of commands that can only be used while in Visual mode or after an operator. The commands that start with "a" select "an "object including white space, the commands starting with "i" select an "inner" object without white space, or just the white space. Thus the "inner" commands always select less text than the "a" commands.
ab "a block" select
ib "inner block" select

Example:

        (defun fib (n)
          (cond ((or (= n 0)
          ;   ^ cursor here
                     (= n 1)) 1)
                (t (+ (fib (- n 1))
                      (fib (- n 2))))))

With the cursor on the d of the cond, "vab" puts Vim in Visual Select mode and selects the entire cond. "vib" puts Vim in Visual Select mode and selects the entire cond, except for its opening and closing parentheses. (I can't see much use for this in Lisp, unless you'd want to delete all the code in a form and start over, e.g. "vibx", or "dib".) Anyway, you can then yank or delete the marked text (with y or x, respectively).

Also, you can just say dab to delete the whole cond form, without marking it first.

(If you don't know about visual block, you should read ":help visual-mode", you'll probably like it.)

Indenting:

={motion} Filter {motion} lines through the external program given with the 'equalprg' option. (When equalprg is empty (the default), Vim uses it's own internal Lisp formatting program, which works pretty well.)

That is, you can put the cursor on a ( and type "=%", and it'll re-indent the current form.

I like to make a mapping to "99[(=%", which re-indents the current top-level form.

== Re-indent the current line. With a count (e.g. "15=="), re-indent that many lines.

Settings:

Here are some settings I think a Lisp programmer would like, or should at least investigate. Almost all of this comes directly from the Vim help files.

set lisp When <Enter> is typed in insert mode set the indent for the next line to Lisp standards (well, sort of). Also happens with "cc" or "S". 'autoindent' must also be on for this to work. The 'p' flag in 'cpoptions' changes the method of indenting: Vi compatible or better.
set autoindent Copy indent from current line when starting a new line
set showmatch When a bracket is inserted, briefly jump to the matching one.
set cpoptions-=m When included, a showmatch will always wait half a second. When not included, a showmatch will wait half a second or until a character is typed. The "-=" removes "m" from the option.

Printing:

See ":help syntax-printing":

Convert the current file to HTML with this command:
:source $VIMRUNTIME/syntax/2html.vim
You will see it crunching away, this can take quite a while for a large file. Some time later another window opens to show the HTML code. Now write this somewhere (doesn't matter where, you throw it away later):
:write main.c.html
Open this file in your favorite browser and print it from there. If all goes well, the output should look exactly as it does in Vim.

The syntax-printing section also describes the ":hardcopy" command, which you can use if you have a PostScript printer.

Folding:

"Folding is used to show a range of lines in the buffer as a single line on the screen. Like a piece of paper which is folded to make it shorter."

I haven't played around much with folding, but

set shiftwidth=1
set foldmethod=indent

has some interesting results.

Example: Screen before folding, using some code I'm fiddling with from Chapter 3 of ANSI Common Lisp:

      /----------
      |;;; exercise 3.2
      |(defun new-union (a b)
      |  (if (not b)
      |    a
      |    (if (member (car b) a)
      |      (new-union a (cdr b))
      |      (new-union (append a (list (car b))) (cdr b)))))
      |
      |(new-union '(a e b c) '(b a d e f g a b c))
      |
      |;;; exercise 3.3
      |(defun occurrences (lst)
      |  (sort (occurrences-aux lst nil)
      |        #'>
      |        :key #'cdr))
      |
      |(defun occurrences-aux (lst assoc)
      |  (if lst
      |    (occurrences-aux (remove (car lst) lst)
      |                     (cons (cons (car lst)
      |                                 (count-em (car lst) lst))
      |                           assoc))
      |    assoc))
      \---------- 

Screen after folding (":set sw=1 fdm=indent" then "zM")

      /----------
      |;;; exercise 3.2
      |(defun new-union (a b)
      |+--  5 lines: (if (not b)----------------------------------------------
      |    
      |(new-union '(a e b c) '(b a d e f g a b c))
      |
      |;;; exercise 3.3 
      |(defun occurrences (lst)
      |+--  3 lines: (sort (occurrences-aux lst nil)--------------------------
      |
      |(defun occurrences-aux (lst assoc)
      |+--  6 lines: (if lst--------------------------------------------------
      |  
      |(defun count-em (x lst)
      |+--  5 lines: (if (null lst)-------------------------------------------
      |
      |(count-em 1 '(0 0 0 1 2 3 1 1 4 5))
      |(setq lst '(a b a d a c d c a)
      |      assoc nil)
      |(setq lst '(a b a a b c d e d a c d c a)
      |      assoc nil)
      |(occurrences nil)
      |(time (occurrences lst))
      \---------- 

See ":help usr_28" and ":help fold.txt".

Sessions & Views:

I haven't played with these much, either.

If you have several Lisp projects going at once, read ":help Session" for information on saving and restoring your loaded files and window layout.

Interacting with a Lisp interpreter

I've written Vim macros and a Perl script that (under Linux, at least) allows you to send Lisp code from Vim to a running Lisp interpreter. At present, it works only with CMU CL running under an XTerm, and has other minimum requirements, but it's a start. See http://vim.sourceforge.net/scripts/script.php?script_id=221.

End.

Copyright © 2002 Larry Clapp. All rights reserved. Permission to copy or transmit this document from this network site for personal or private use semantically unmodified & in its entirety, is granted. Permission to store this document or copies of it, semantically unmodified & in its entirety, is granted.