[Top][All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Better indentation for elisp

From: Sergey Mozgovoy
Subject: Better indentation for elisp
Date: Mon, 18 Feb 2013 11:59:27 -0800 (PST)

The problem

Standard indentation algorithm for Emacs Lisp is not perfect in some
respects.  For example, it fails to indent code like this:
(flet ((sum (x y)
                (+ x y))
        (diff (x y)
               (- x y)))
or like this:
(func-1 (or arg-1
                arg-2) weird-arg
and this:
(let (var-1 var-2
               (x 10))
  (+ var-1 var-2))

SLIME indentation

Those who use SLIME know that there's a contrib *slime-cl-indent*
which does some nice job of indenting Common Lisp code.  Aside from
some CL-specific things (like the loop macro or tagbody), SLIME
indentation is different from that of Elisp in that it supports
arbitrarily-nesting indentation patterns rather than simple values.

For example, here's indentation spec for *case* (quoted from
docstring for *common-lisp-indent-function*):
... the function `case' has an indent property (4 &rest (&whole 2 &rest 1)),
  * indent the first argument by 4.
  * arguments after the first should be lists, and there may be any number
    of them.  The first list element has an offset of 2, all the rest
    have an offset of 2+1=3.

SLIME => Emacs Lisp

I think we could just reuse some SLIME indentation ideas for Emacs
Lisp.  The algorithm is smart enough to handle any typical Elisp
indentation, and it is kind of a generalization of the present one.
General idea is this: walk nested lists up not more than some depth
limit (*lisp-indent-maximum-backtracking*) and see what head
symbols (*car*s) of ancestor lists say about indentation.  Upper
specs override what was said by more local ones.  Whatever the current
indentation we have when the depth is exhausted becomes our end

One thing that I found odd in SLIME indentation is that they specify
absolute offsets (in spaces).  I don't see why such a precision might
be really needed.. to my mind, indentation should better be specified
in terms of number of distinguished forms, like it is implemented now
for Elisp.  Maybe I just don't get it.

So I propose the following indentation patterns, roughly:
  * any valid value for *lisp-indent-function* symprop, meaning
    is preserved.  This includes:
      + natural number (# of distinguished args);
      + *defun* symbol which means to indent in defun-style;
      + *nil* stands for "usual" indentation, i.e. indent like a
        normal function call form;
      + *:flat* -- new thing which tells that list should be
        flat, no indentation for any args;
  * (*S* . (*P-1* *P-2* ... *P-N*)), where *S* is a simple pattern
    (any of above), and all *P-i* are any other patterns (recursive
    *S* specifies indentation for the enclosing list. b>P-1*, ..., *P-n*
    indentation for sublists, beginning from the second one ("first
    function argument");

  * (*S* . (*P-1* ... *P-N* &rest *P*)), same as above but *P*
    applies to all the other sublists after *N*.

So for example, we'd give indentation for the above-mentioned forms
like this:
(put 'flet 'lisp-indent-function '(1 (:flat &rest defun)))
(put 'let 'lisp-indent-function '(1 (:flat &rest 1)))
(put 'cond 'lisp-indent-function '(nil &rest :flat))
(Yes, I do agree that *cond* is indented perfectly now, this was
just for the sake of example.)

What is done so far

I decided to just go ahead and try to write some Elisp indentation

Actually, it seems to work fine on my laptop for me.  I'd be happy if
Emacs developers find my indentation actually useful and incorporate
some of it into Emacs.

If someone gets really interested in the idea of improving Emacs Lisp
indentation, I'm always eager to share my work.

View this message in context: 
Sent from the Emacs - Dev mailing list archive at Nabble.com.

reply via email to

[Prev in Thread] Current Thread [Next in Thread]