8 Editing Lisp

Hemlock provides a large number of powerful commands for editing Lisp code. It is possible for a text editor to provide a much higher level of support for editing Lisp than ordinary programming languages, since its syntax is much simpler.

8.1 Lisp Mode

Lisp mode is a major mode used for editing Lisp code. Although most Lisp specific commands are globally bound, Lisp mode is necessary to enable Lisp indentation, commenting, and parenthesis-matching. Whenever the user or some Hemlock mechanism turns on Lisp mode, the mode's setup includes locally setting Current Package (see section 9.3) in that buffer if its value is non-existent there; the value used is "USER".

Lisp Mode [Command]

This command sets the major mode of the current buffer to Lisp.

8.2 Form Manipulation

These commands manipulate Lisp forms, the printed representations of Lisp objects. A form is either an expression balanced with respect to parentheses or an atom such as a symbol or string.

Forward Form (bound to C-M-f) [Command]

Backward Form (bound to C-M-b) [Command]

Forward Form moves to the end of the current or next form, while Backward Form moves to the beginning of the current or previous form. A prefix argument is treated as a repeat count.

Forward Kill Form (bound to C-M-k) [Command]

Backward Kill Form (bound to C-M-Delete, C-M-Backspace) [Command]

Forward Kill Form kills text from the point to the end of the current form. If at the end of a list, but inside the close parenthesis, then kill the close parenthesis. Backward Kill Form is the same, except it goes in the other direction. A prefix argument is treated as a repeat count.

Mark Form (bound to C-M-@) [Command]

This command sets the mark at the end of the current or next form.

Transpose Forms (bound to C-M-t) [Command]

This command transposes the forms before and after the point and moves forward. A prefix argument is treated as a repeat count. If the prefix argument is negative, then the point is moved backward after the transposition is done, reversing the effect of the equivalent positive argument.

Insert () (bound to M-() [Command]

This command inserts an open and a close parenthesis, leaving the point inside the open parenthesis. If a prefix argument is supplied, then the close parenthesis is put at the end of the form that many forms from the point.

Extract Form [Command]

This command replaces the current containing list with the next form. The entire affected area is pushed onto the kill ring. If an argument is supplied, that many upward levels of list nesting is replaced by the next form. This is similar to Extract List, but this command is more generally useful since it works on any kind of form; it is also more intuitive since it operates on the next form as many Lisp mode commands do.

8.3 List Manipulation

List commands are similar to form commands, but they only pay attention to lists, ignoring any atomic objects that may appear. These commands are useful because they can skip over many symbols and move up and down in the list structure.

Forward List (bound to C-M-n) [Command]

Backward List (bound to C-M-p) [Command]

Forward List moves the point to immediately after the end of the next list at the current level of list structure. If there is not another list at the current level, then it moves up past the end of the containing list. Backward List is identical, except that it moves backward and leaves the point at the beginning of the list. The prefix argument is used as a repeat count.

Forward Up List (bound to C-M-)) [Command]

Backward Up List (bound to C-M-(, C-M-u) [Command]

Forward Up List moves to after the end of the enclosing list. Backward Up List moves to the beginning. The prefix argument is used as a repeat count.

Down List (bound to C-M-d) [Command]

This command moves to just after the beginning of the next list. The prefix argument is used as a repeat count.

Extract List (bound to C-M-x) [Command]

This command "extracts" the current list from the list which contains it. The outer list is deleted, leaving behind the current list. The entire affected area is pushed on the kill ring, so that this possibly catastrophic operation can be undone. The prefix argument is used as a repeat count.

8.4 Defun Manipulation

A defun is a list whose open parenthesis is against the left margin. It is called this because an occurrence of the defun top level form usually satisfies this definition, but other top level forms such as a defstruct and defmacro work just as well.

End of Defun (bound to C-M-e, C-M-]) [Command]

Beginning of Defun (bound to C-M-a, C-M-[) [Command]

End of Defun moves to the end of the current or next defun. Beginning of Defun moves to the beginning of the current or previous defun. End of Defun will not work if the parentheses are not balanced.

Mark Defun (bound to C-M-h) [Command]

This command puts the point at the beginning and the mark at the end of the current or next defun.

8.5 Indentation

One of the most important features provided by Lisp mode is automatic indentation of Lisp code. Since unindented Lisp is unreadable, poorly indented Lisp is hard to manage, and inconsistently indented Lisp is subtly misleading. See section 7.2 for a description of the general-purpose indentation commands. Lisp mode uses these indentation rules:

  • If in a semicolon (;) comment, then use the standard comment indentation rules. See page 7.1.
  • If in a quoted string, then indent to the column one greater than the column containing the opening double quote. This is exactly what you want in function documentation strings and wrapping error strings.
  • If there is no enclosing list, then use no indentation.
  • If enclosing list resembles a call to a known macro or special-form, then the first few arguments are given greater indentation and the first body form is indented two spaces. If the first special argument is on the same line as the beginning of the form, then following special arguments will be indented to the start of the first special argument, otherwise all special arguments are indented four spaces.
  • If the previous form starts on its own line, then the indentation is copied from that form. This rule allows the default indentation to be overridden: once a form has been manually indented to the user's satisfaction, subsequent forms will be indented in the same way.
  • If the enclosing list has some arguments on the same line as the form start, then subsequent arguments will be indented to the start of the first argument.
  • If the enclosing list has no argument on the same line as the form start, then arguments will be indented one space.

Indent Form (bound to C-M-q) [Command]

This command indents all the lines in the current form, leaving the point unmoved. This is undo-able.

Fill Lisp Comment Paragraph [Command]

Fill Lisp Comment Paragraph Confirm (initial value t) [Variable]

This fills a flushleft or indented Lisp comment. This also fills Lisp string literals using the proper indentation as a filling prefix. When invoked outside of a comment or string, this tries to fill all contiguous lines beginning with the same initial, non-empty blankspace. When filling a comment, the current line is used to determine a fill prefix by taking all the initial whitespace on the line, the semicolons, and any whitespace following the semicolons.

When invoked outside of a comment or string, this command prompts for confirmation before filling. It is useful to use this for filling long export lists or other indented text or symbols, but since this is a less common use, this command tries to make sure that is what you wanted. Setting Fill Lisp Comment Paragraph Confirm to nil inhibits the confirmation prompt.

Defindent (bound to C-M-#) [Command]

This command prompts for the number of special arguments to associate with the symbol at the beginning of the current or containing list.

Indent Defanything (initial value 2) [Variable]

This is the number of special arguments implicitly assumed to be supplied in calls to functions whose names begin with "def". If set to nil, this feature is disabled.

Move Over ) (bound to M-)) [Command]

This command moves past the next close parenthesis and then does the equivalent of Indent New Line.

8.6 Parenthesis Matching

Another very important facility provided by Lisp mode is parenthesis matching. Two different styles of parenthesis matching are supported: highlighting and pausing.

Highlight Open Parens (initial value t) [Variable]

Open Paren Highlighting Font (initial value nil) [Variable]

When Highlight Open Parens is true, and a close paren is immediately before the point, then Hemlock displays the matching open paren in Open Paren Highlighting Font.

Open Paren Highlighting Font is the string name of the font used for paren highlighting. Only the "(" character is used in this font. If null, then a reasonable default is chosen. The highlighting font is read at initialization time, so this variable must be set before the editor is first entered to have any effect.

Lisp Insert ) [Command]

Paren Pause Period (initial value 0.5) [Variable]

This command inserts a close parenthesis and then attempts to display the matching open parenthesis by placing the cursor on top of it for Paren Pause Period seconds. If there is no matching parenthesis then beep. If the matching parenthesis is off the top of the screen, then the line on which it appears is displayed in the echo area. Paren pausing may be disabled by setting Paren Pause Period to nil.

The initial values shown for Highlight Open Parens and Paren Pause Period are only approximately correct. Since paren highlighting is only meaningful in Lisp mode, Highlight Open Parens is false globally, and has a mode-local value of t in Lisp mode. It it redundant to do both kinds of paren matching, so there is also a binding of Paren Pause Period to nil in Lisp mode.

Paren highlighting is only supported under X windows, so the above defaults are conditional on the device type. If Hemlock is started on a terminal, the initialization code makes Lisp mode bindings of nil and 0.5 for Highlight Open Parens and Paren Pause Period. Since these alternate default bindings are made at initialization time, the only way to affect them is to use the after-editor-initializations macro.

8.7 Parsing Lisp

Lisp mode has a fairly complete knowledge of Lisp syntax, but since it does not use the reader, and must work incrementally, it can be confused by legal constructs. Lisp mode totally ignores the read-table, so user-defined read macros have no effect on the editor. In some cases, the values the Lisp Syntax character attribute can be changed to get a similar effect.

Lisp commands consistently treat semicolon (;) style comments as whitespace when parsing, so a Lisp command used in a comment will affect the next (or previous) form outside of the comment. Since #| ... |# comments are not recognized, they can used to comment out code, while still allowing Lisp editing commands to be used.

Strings are parsed similarly to symbols. When within a string, the next form is after the end of the string, and the previous form is the beginning of the string.

Defun Parse Goal (initial value 2) [Variable]

Maximum Lines Parsed (initial value 500) [Variable]

Minimum Lines Parsed (initial value 50) [Variable]

In order to save time, Lisp mode does not parse the entire buffer every time a Lisp command is used. Instead, it uses a heuristic to guess the region of the buffer that is likely to be interesting. These variables control the heuristic.

Normally, parsing begins and ends on defun boundaries (an open parenthesis at the beginning of a line). Defun Parse Goal specifies the number of defuns before and after the point to parse. If this parses fewer lines than Minimum Lines Parsed, then parsing continues until this lower limit is reached. If we cannot find enough defuns within Maximum Lines Parsed lines then we stop on the farthest defun found, or at the point where we stopped if no defuns were found.

When the heuristic fails, and does not parse enough of the buffer, then commands usually act as though a syntax error was detected. If the parse starts in a bad place (such as in the middle of a string), then Lisp commands will be totally confused. Such problems can usually be eliminated by increasing the values of some of these variables.

Parse Start Function (initial value start-of-parse-block) [Variable]

Parse End Function (initial value end-of-parse-block) [Variable]

These variables determine the region of the buffer parsed. The values are functions that take a mark and move it to the start or end of the parse region. The default values implement the heuristic described above.