cl-indentify

2023-02-15

A code beautifier for Common Lisp.

Upstream URL

github.com/yitzchak/cl-indentify

Author

Tarn W. Burton

License

MIT
README

cl-indentify

A library and command line utility to automatically indent Common Lisp source files.

Installation

For command line usage cl-indentify currently requires roswell. Once roswell is installed enter the following in a shell.

$ ros install yitzchak/cl-indentify

Functionality

Inspired by scmindent/lispindent cl-indentify attempts to deal with more complex indention by using with a template system that can specify the indention characteristics of subforms in addition to the top level form.

Templates are generally selected by the head form of a list. For example, in the following cl-indentify will attempt to select a template with a key name of "LET".

(let
    ((fu 1)
     (bar 2))
  (+ fu bar))

Templates can configure a number of aspects of the indention characteristic, but the main behavior is controlled by the template :style key.

:call Style

The default style is :call which calculates indention based a fixed length "primary" group and a variable "secondary" group. For example, with-slots is defined to have two subforms in the primary group and the remaining in the secondary group. The primary group is indented by four columns and the secondary group is indented by two columns by default.

(with-slots
    (fu bar) wibble
  (quux bar)
  (+ fu bar))

If the first subform is on the same line as with-slots the following subforms that begin on a newline are aligned with the first subform.

(with-slots (fu bar)
            wibble
  (quux bar)
  (+ fu bar))

Likewise, if the first secondary subform is on the same line as the last primary subform the following subforms that begin on a newline are aligned with the first secondary subform. This can be seen in the case of if which has a primary count of one. Normally would be indented as

(if fu
  bar
  quux)

When the "then" class is on the same line as the test expression then the "else" clause will be aligned to the "then" clause.

(if fu bar
       quux)

If a template cannot be found for the head subform then the :call style is assumed with a primary count of zero.

The template for subforms can be explicitly specified using the :sub key. The value is a list of templates or a nil if the default template should be used. For :call and :tag style the first item in the list should always be a nil as this is the template for head subform. This is only used for :list styles. If the length of the :sub value is less then the length of the form then the last item in :sub will be used.

For example, the template for cond is the following, which specifies that all secondary group subforms will have a style of :call with a primary count of zero.

(:style :call
 :count 0
 :sub (nil
       (:style :call
        :count 0)))

This results in

(cond
  ((not ch)
    :eof)
  ((char= ch #\!)
    :bang)
  (t
    ch))

:tag Style

:tag style behaves exactly as :call in respect to primary and secondary groups, but will align tag names that appear in special forms such as tagbody with the head subform. For example do*

(do* ((pos 0 (1+ pos))
      (wibble 7))
     ((= pos 20))
  (when (= pos 20)
    (go fu))
  (format t "~A~%" pos)
 fu
  (format t "~A~%" wibble))

:quote Style

:quote style will indent the form and any subforms as if it a quoted literal. List subforms will aligned the head subform.

(1
 2
 3)

'(fu bar
  3
  (wibble
   quux))

:list Style

:list style is like :quote style but subforms will be indented according to their own templates or may be specified by the :sub key as in :call. For example, let has its primary subform style as :list with the following template

(:style :call
 :count 1
 :sub (nil
       (:style :list
        :sub ((:style :call :count 0)))
       nil))

This results in

(let (quux
      (fu 1)
      (bar
        (wibble 7)))
  (+ fu bar))

Command Line Usage

Using cl-indentify as a stdin/stdout filter is simple.

$ echo bar.lisp | cl-indentify

To read directly from a file just specify the filename on the command line.

$ cl-indentify bar.lisp

The output can be sent to a specific file versus stdout via the --outfile option.

$ cl-indentify -o fu.lisp bar.lisp

Multiple files can be processed at one time by specifying them on the command line. The output will be sent to the file in the --outfile option or stdout unless the --replace option is used to replace the original files.

$ cl-indentify -r fu.lisp bar.lisp

Configuration

Unless the --no-defaults option is specified then cl-indentify will load a set of default templates. After these templates are loaded then cl-indentify will look in the user's config home for a template file according the XDG specification. For instance, on Linux this would be ~/.config/cl-indentify/templates.lisp, which can be supressed by using the --no-user option. Additional template files can be loaded using the --templates options. For example, the following suppresses defaults and user templates and loads the template files wibble and quux.

$ cl-indentify --no-defaults --no-user --templates wibble -t quux fu.lisp

Template files should be in Sexpr format with each item a list that specifies the indention characteristics of a function or macro. For instance, the following specifies that defun has a primary count of two. It also specifies that defmethod has a primary count of two but that quantifiers :before, :after, and :around should be ignored in accumulating the primary count. case is defined to have a primary count of one and secondary forms are defined to have a primary count of zero.

(defun :style :call
       :count 2
       :sub (nil nil
             (:style :list)
             nil))

(defmethod :style :call
           :count 2 
           :ignore (:before :after :around) 
           :sub ((:style :list)
                 (:style :list)
                 nil))

(case :style :call
      :count 1 
      :sub (nil nil 
            (:style :call :count 0)))

Editor Usage

Text editors often provide the ability to interface with an external code filter/beautifier. The following are instructions for various editors.

Gnome Builder Usage

Create a file ~/.config/gnome-builder/beautifier_plugin/commonlisp/config.ini with the following contents. Please note that GtkSourceView must support Common Lisp syntax highlighting (v4.5.91).

[global]
default = indentify

[indentify]
command-pattern = cl-indentify @s@
name = Indentify

Vim Usage

To set a local variant of the equal command based on extension add the following to ~/.vimrc or approriate config file.

autocmd bufread,bufnewfile *.lisp,*.asd,*.ros setlocal equalprg=cl-indentify

If you are using the filetype plugin you can use the following

autocmd filetype lisp setlocal equalprg=cl-indentify

Dependencies (6)

  • alexandria
  • command-line-arguments
  • rove
  • trivial-escapes
  • trivial-gray-streams
  • uiop

Dependents (1)

  • GitHub
  • Quicklisp