npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

scmindent

v1.0.4

Published

filter for indenting Racket, Scheme, Common Lisp, LFE, ART*Enterprise, etc

Downloads

6

Readme

= Editing Lisp and Scheme files in vi

“But then, when was the last time you heard of a lisp programmer that used vi?” — Paul Fox, vile developer

❧❧❧

The ability to automatically indent code is indispensable when editing files containing s-expression-based code such as Racket, Common Lisp, Scheme, LFE, ART*Enterprise, and other Lisps.

The text editor family vi provides the option lisp, which in conjunction with the options autoindent and showmatch provides a form of Lisp indenting, but except in the improved vi clones Vim and Neovim, this support is poor in at least two respects:

  1. escaped parentheses and double-quotes are not treated correctly; and

  2. all head-words are treated identically.

Even the redoubtable Vim, which has improved its Lisp editing support over the years, and provides the lispwords option, continues to fail in link:./vim-indent-error.lisp[strange ways].

Fortunately, both vi and Vim let you delegate the responsibility for indenting such code to an external filter program of your choosing. I provide here three such filtering scripts: link:./scmindent.rkt[scmindent.rkt] written in Racket, link:./lispindent.lisp[lispindent.lisp] in Common Lisp, and link:./scmindent.js[scmindent.js] in JavaScript.

== Installation

The Racket and CL scripts are operationally identical and use the same type of customization via the file ~/.lispwords.

The JavaScript version differs only in that its customization file is named link:./lispwords.json[lispindent.json] and uses JSON.

To use either of scmindent.rkt, lispindent.lisp, or scmindent.js, place them in your PATH. Alternatively, the JavaScript version is also available as a Node package:


sudo npm install -g scmindent

This directly installs the executable scmindent in your PATH.

Henceforth, I will refer to just scmindent.rkt with the understanding that everything mentioned applies equally to lispindent.lisp, scmindent.js, and the NPM version scmindent.

== Usage

scmindent.rkt takes Lisp text from its standard input and produces an indented version thereof on its standard output. (Thus, it is possible to use scmindent.rkt as a command-line filter to “beautify” Lisp code, even if you don’t use vi.)

In Vim, set the equalprg option to the filter name, which causes the indenting command = to invoke the filter rather than the built-in indenter.

You might want to make the equalprg setting local to a file based on its extension:


autocmd bufread,bufnewfile .lisp,.scm setlocal equalprg=scmindent.rkt

or its filetype:


autocmd filetype lisp,scheme setlocal equalprg=scmindent.rkt

In vi’s other than Vim, use the ! command to invoke the filter on part or all of your buffer: Type ! to declare you’ll be filtering; a movement command to scoop up the lines you’ll be filtering; then the filter name (scmindent.rkt) followed by Return.

== How subforms are indented

Lisp indentation has a tacit, widely accepted convention that is not lightly to be messed with, so scmindent.rkt strives to provide the same style as emacs, with the same type of customization.

By default, the indentation procedure treats a form split over two or more lines as follows. (A form, if it is a list, is considered to have a head subform and zero or more argument subforms.)

1: If the head subform is followed by at least one other subform on the same line, then subsequent lines in the form are indented to line up directly under the first argument subform.


(some-user-function-1 arg1 arg2 ...)

2: If the head subform is a list and is on a line by itself, then subsequent lines in the form are indented to line up directly under the head subform.


((some-user-function-2) arg1 arg2 ...)

3: If the head subform is a symbol and is on a line by itself, then subsequent lines in the form are indented one column past the beginning of the head symbol.


(some-user-function-3 arg1 arg2 ...)

4: If the head form can be deduced to be a literal, then subforms on subsequent lines line up directly under it, e.g.


(1 2 3 4 5 6)

'(alpha beta gamma)

== Keywords

However, some keyword symbols are treated differently. Each such keyword has a number N associated with it called its Lisp indent number, which influences how its subforms are indented. This is almost exactly analogous to emacs’s lisp-indent-function, except I’m using numbers throughout.

If the i’th argument subform starts on a subsequent line, and i <= N, then it is indented 3 columns past the keyword. All subsequent subforms are indented simply one column past the keyword.


(defun some-user-function-4 (x y) ;defun is a 2-keyword body ...)

(defun some-user-function-5 (x y) body ...)

(if test ;if is also a 2-keyword then-branch else-branch)

scmindent.rkt pre-sets the indent numbers of many well-known Lisp keywords. In addition, any symbol that starts with def and whose indent number has not been explicitly set is assumed to have an indent number of 0.

== Customization

You can specify your own Lisp indent numbers for keywords in the file .lispwords in your home directory. ~/.lispwords can contain any number of lists: The first element of each list is the Lisp indent number that is applied to the symbols in the rest of the list. E.g.,


(0 begin0) (1 when unless) (2 do defun)

This assigns a Lisp indent number of 0 to begin0; 1 to when and unless; and 2 to do and defun. If using the JavaScript scmindent, see below for the corresponding lispwords.json format.

(Note that in contrast to Vim’s flat list of lispwords, ~/.lispwords allows for different categories of lispwords. Vim’s lispwords are all of Lisp indent number 0.)

For example, a lot of users prefer the keyword if to have its then- and else-clauses indented the same amount of 3 columns. I.e., they want it to be a 3-keyword. A .lispwords entry that would secure this is:


(3 if)

To remove the keywordness of a symbol, you can assign it a Lisp indent number < 0. E.g.


(-1 if)

would also cause all of if’s subforms to be aligned. (This is because −1 causes subforms on subsequent lines to line up against the first argument subform on the first line, and that happens to be 3 columns past the beginning of a 2-column keyword like if. The only difference between −1 and 3 here is what happens when the if is on a line by itself, with the test on the line following. −1 indents subsequent lines one column past the beginning of the if, whereas 3 continues to indent them three columns past the beginning of the if. Further differences emerge between 3 and −1 when the if has more than three argument subforms, as allowed by emacs lisp, where 2 and −1 immediately prove to be better choices than 3. The author has made 2 the default because it is the only option that has the merit of indenting the then- and else-subforms by differing amounts.)

== Customization (lispwords.json)

lispwords.json, used by the JavaScript version, employs a slightly more verbose format than .lispwords in order to accommodate JSON. Keywords are specified as keys, the Lisp indent numbers as values, and keywords sharing the same Lisp indent number cannot be bunched. E.g., the example .lispwords of the previous section will be specified as follows in lispwords.json:


{ "begin0": 0, "when": 1, "unless": 1, "do": 2, "defun": 2 }

❧❧❧

Last modified: 2015-11-13