Skip to content

Instantly share code, notes, and snippets.

@pesterhazy
Last active January 15, 2019 02:14
Show Gist options
  • Save pesterhazy/aac9638f1d22a3bc3339f95d1c607aed to your computer and use it in GitHub Desktop.
Save pesterhazy/aac9638f1d22a3bc3339f95d1c607aed to your computer and use it in GitHub Desktop.
Customizing zprint

The zprint auto-formatter for Clojure comes with built-in defaults, but it is also highly customizable. The docs can be a bit daunting, so in what follows I will explain how to set some configuration options that are commonly used.

defn

By default, zprint will move the arg vector of defn forms to a separate line:

$ zprintm-0.4.12 <<< $'(defn discombobulate-widget [x]\n(foo foo foo foo foo)\n(bar bar bar bar bar bar bar))'
(defn discombobulate-widget
  [x]
  (foo foo foo foo foo)
  (bar bar bar bar bar bar bar))

The default for defn is :arg1-body. You can make zprint keep the first argument on the same line as the function name (if it fits) by setting the "defn" key in :fn-map to :arg2:

$ zprintm-0.4.12 '{:fn-map {"defn" :arg2}}' <<< $'(defn discombobulate-widget [x]\n(foo foo foo foo foo)\n(bar bar bar bar bar bar bar))'
(defn discombobulate-widget [x]
  (foo foo foo foo foo)
  (bar bar bar bar bar bar bar))

Meaningful vectors

In Hiccup, Reagent or Sablono, HTML elements are represented by a vector:

(defn nav-ui
  []
  [:nav
   [:a {:href "#/"} "Home"]
   [:span " "]
   [:a {:href "#/users"} "Users"]
   [:span " "]
   [:a {:href "#/users/1"} "User #1"]
   [:span " "]
   [:a {:href "#/users/999"} "Invalid user"]
   [:span " "]
   [:a {:href "#/clock"} "Clock"]])

With default settings, zprint will will reformat the vector as if it were pure data, trying to fill the page width with elements:

$ zprintm-0.4.12 <<< $'(defn nav-ui [x]\n  [:nav\n    [:a {:href "#/"} "Home"]])'
(defn nav-ui [x] [:nav [:a {:href "#/"} "Home"]])

To avoid this, you can instruct zprint to respect newlines in vectors starting with a keyword:

$ zprintm-0.4.12 '{:style :keyword-respect-nl}' <<< $'(defn nav-ui [x]\n  [:nav\n    [:a {:href "#/"} "Home"]])'
(defn nav-ui
  [x]
  [:nav
   [:a {:href "#/"} "Home"]])

You can also apply this setting for only a single form.

Line width

By default, zprint assumes a line width of 80 characters. The top-level option :width allows you to customize this number. This can be useful for experimenting with options. Often formatting becomes interesting as soon as a form doesn't fit in a single line. For example, this is boring:

$ zprintm-0.4.12 '{}' <<< $'(defn square [n] (* n n))'
(defn square [n] (* n n))

But by setting an artificially short line width, we can cause zprint to introduce newlines:

$ zprintm-0.4.12 '{:width 15}' <<< $'(defn square [n] (* n n))'
(defn square
  [n]
  (* n n))

Top-level newline characters

Zprint newline handling may seem confusing at first glance but makes sense once you understand it.

A source file is a sequence of top-level forms (such as defns) separated by newlines. In general, zprint's newline philosophy can be summarized like this. On the one hand, it doesn't touch the whitespace outside the top-level forms. Newlines and other whitespace inside the forms, on the other hand, is mostly ignored and reformatted using its layouting algorithm. That's a powerful property: you can write function definitions without any regard to style or indentation. You can be confident that zprint will clean up after you.

So re-organizing top-level newlines is not part of zprint's primary job. If you still want to use it to normalize newlines, see the :parse option contained in this answer.

Commas in maps

By default, zprint separates k/v-pairs in maps with commas. But you can turn that behavior off:

Before:

$ zprintm-0.4.12 <<< $'{"a" "b" "c" "d"}'
{"a" "b", "c" "d"}

After:

$ zprintm-0.4.12 '{:map {:comma? false}}' <<< $'{"a" "b" "c" "d"}'
{"a" "b" "c" "d"}
@kkinnear
Copy link

This is great! The only thing I can see is that defn could be :arg2 to keep the arg vector on the same line (or try to). But :fn is probably as good. I don't know if you want to mention the community style. I don't use it, obviously, but somebody must like it?

@pesterhazy
Copy link
Author

@kkinnear, good point, I've changed :fn to :arg2

@kkinnear
Copy link

I would be delighted to put this in zprint/doc/quickstart.md or whatever you think it should be called, if that made sense to you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment