deniz.in

Markets

Weather

Loading weather

· via Hacker News – Front Page (native)

Sift brings shared formatter, linter and language server to Scheme, Emacs Lisp and Common Lisp

A new MIT-licensed tool called Sift provides idempotent formatting, a 92-rule linter and a Language Server Protocol implementation for Scheme, Emacs Lisp and Common Lisp.

Sift brings shared formatter, linter and language server to Scheme, Emacs Lisp and Common Lisp

One tool for three dialects

A project called Sift has reached the front page of Hacker News with an ambitious pitch for the Lisp family: a single program that formats, lints and serves three dialects — Scheme, Emacs Lisp and Common Lisp. According to the project's GitHub page, Sift is written in Guile 3.0 using only its standard library, with no C dependency and no build step. It installs through Guix or Nix, or a plain clone of the repository, and ships as one executable plus an Emacs package under the MIT license.

The design decision that carries the rest, as the README puts it, is that a Lisp file is a tree of forms with source positions attached, not a sequence of lines. Sift parses that tree and prints it back, which is what lets it make precise guarantees about what formatting will and will not change.

Formatter guarantees

Two properties are asserted in the test suite over every fixture. The first is idempotence: running the formatter over an already formatted file returns it unchanged. The second is preservation — comments, blank lines, datum comments written #;, read-time forms written #. and similar awkward constructs come out byte-exactly where they went in.

Each dialect is parsed by its own rules rather than by a compromise grammar. The README gives a concrete example: square brackets denote a vector in Emacs Lisp but a clause in Scheme, and Sift follows each dialect's own specification instead of splitting the difference. Guile-specific reader syntax such as #{ ... }# symbols is handled as well, since Guile is the dialect the tool itself is written in; the other two dialects are read from their specifications.

Check, format, lint, fix

The tool separates its jobs into distinct commands. sift check prints the patch it would apply and exits with status 1, which makes it usable as a CI gate. sift format prints the formatted file, and adding --write saves it. sift lint names the rule that fired, its severity and its position, while lint --fix applies repairs. A sift ci command answers the formatting and lint questions in one pass without touching the tree.

The linter comes with 92 rules. Each carries a name, a stable code such as SIFT012, a rationale, the dialects it applies to, an example and its own test file. sift explain reads a rule's reasoning out of the program itself, and the rules documentation is generated from the same records, so the docs cannot drift from the code they describe. Fixes are deliberately conservative: a fix replaces exactly one range, no delimiter is invented and no identifier is renamed, and if the combined fixes of a run would introduce a parse error, all of them are discarded. Suppression lives in comments next to the code — disabling one rule for one line, for the enclosing form, or switching it back on — rather than in a configuration list that code review never reaches.

A language server and machine-readable output

The same program speaks the Language Server Protocol over standard input and output, so Eglot in Emacs, Neovim and any other LSP client receive the same findings and fixes a command-line pipeline does. Beyond diagnostics and formatting, the server offers go-to-definition and references — including names defined by the standard library and installed packages, documentation an implementation compiled in, and the compressed sources most Emacs installations carry — plus an outline, completion, inlay hints that name a call's arguments, hover for symbols and rules, import organization, file renames that keep the module in step, and a second opinion from the dialect's own compiler when a buffer is saved.

For pipelines, output can be a compile-style line that Emacs compilation mode parses, JSON, SARIF 2.1.0, GitHub pull-request annotations, or silence for scripts that only check the exit code. Configuration lives in YAML across four layers from system to command line, and sift config reports which layer won. sift install-hooks writes a pre-commit hook that runs the CI check, and sift doctor reports the interpreter, library, rule count and active configuration.

Why it matters

Mainstream languages treat formatters, linters and language servers as table stakes, while the Lisp family has long relied on piecemeal, per-dialect solutions. Sift's bet is that one exact formatter-linter-server, built on the fact that Lisp code is a data structure before it is text, can serve students, Emacs package authors, Guix contributors and polyglot repositories with a single executable, a single configuration file and a single CI step. Its preservation and idempotence guarantees, asserted across the test suite, target the most common complaint about automatic formatters — that they mangle comments and churn diffs. Adoption is the open question, but the MIT license and the zero-build Guile implementation lower the barrier for anyone who wants to try it.

  • #lisp
  • #scheme
  • #emacs-lisp
  • #language-server-protocol
  • #developer-tools
  • #open-source

Related posts