/formatting-stack

An efficient, smart, graceful composition of Clojure formatters, linters and such.

Primary LanguageClojureEclipse Public License 2.0EPL-2.0

formatting-stack CircleCI

formatting-stack is a formatting/linting solution that can be integrated with:

  • your Component (or Integrant, or bare tools.namespace) system
    • for instantaneous performance
      • no cold-starts!
    • and precise understanding of your codebase
      • powered by Clojure's introspection capabilities (reified vars, namespaces), and occasionally eval
    • and a reasonable triggering frequency
      • so you don't format too frequently (format-on-save), or not frequently enough (git pre-commit hook)
  • Git status/branch information
    • for some performance gains (typically only added/changed files will be processed)
    • and also for gradual formatting
  • Anything you want
    • A vanilla repl, Lein task, CI workflow...

As of today, it is integrated with:

And it also bundles a few tiny linters of its own:

It is fully extensible: you can configure the bundled formatters, remove them, and/or add your own.

Whenever it's safe, each formatter/linter will make full use of your CPU's cores.

Linters' reports are presented under a unified format.

Smart code analysis

As mentioned, formatting-stack understands your codebase and its dependencies. It knows which vars in the project are macros. It also reads the metadata of all function/macro vars.

It also associates :style metadata to your project's vars, in a clean manner, when needed.

Armed with those powers, we can do two nifty things:

  • Inform cljfmt of indentation through metadata + config, using the CIDER indent spec format (by default, using an heuristic for cider->cljfmt format translation) or the cljfmt format (as a fallback).
    • Recommendation: use metadata for your own code, use config for third-party code (that may be awaiting for a pull request)
  • Inform CIDER of indentation rules through config
    • CIDER understands either metadata or emacs-specific rules, but not config

You can find examples of how to do such configuration in the wiki.

Graceful git strategies

Git integration is documented at formatting-stack.strategies.

The general intent is to make formatting:

  • Efficient
    • don't process non-touched files
  • Gradual
    • don't format the whole project at once
    • favor reviewable diffs - nobody can review (or learn from) whole-project diffs
  • Safe
    • only format code that is completely staged by git
      • else any bug in formatting code could destroy your unsaved changes

...that's the default Git strategy anyway, apt for repl-driven development. You are free to supply an alternative strategy.

Commonly needed alternative strategies are offered/documented in branch-formatter and project-formatter.

Consolidated reporting

As you can see in the screenshot, formatting-stack presents linters' outputs under a hierarchical, file-grouped format.

Screenshot 2020-02-19 at 07 04 38

Alternative reporters can be found in ./src/formatting_stack/reporters, such as

  • formatting-stack.pretty-line-printer offers more concise output
  • formatting-stack.file-writer offers a file-output instead of stdout

Installation

Coordinates

[formatting-stack "4.6.0"]

Also you might have to add the refactor-nrepl dependency.

  • If you use tooling like CIDER, typically this dependency will be already injected into your classpath, so no action required in this case.
  • Else, please add the latest version to your project (or personal profile).
  • If this dependency isn't added, formatting-stack will degrade gracefully, using slightly fewer formatters/linters.

Reloaded Workflow integration

  • If you use the Component component, then com.stuartsierra.component.repl/reset will use formatting-stack, applying all its formatters/linters.
  • If you use the Integrant component, then integrant.repl/reset will use formatting-stack, applying all its formatters/linters.

The above can be good enough. However resetting your system can be somewhat expensive, and you may find yourself using clojure.tools.namespace.repl/refresh instead.

For that case, you can create some facility (e.g. shortcut, snippet) for the following code:

(clojure.tools.namespace.repl/refresh :after 'formatting-stack.core/format!)

Vanilla integration

formatting-stack.core/format! is a plain function, considered a public API, that is safe to invoke over REPL, a script, or anything you please.

See also: format-and-lint-branch!, format-and-lint-project!.

Advanced configuration

If you want to add custom members to the format! options (namely: :formatters, or :strategies, etc), a typical pattern would be:

(formatting-stack.core/format! :formatters (conj formatting-stack.defaults/default-formatters my-custom-formatter))

You can also pass [] for disabling a particular aspect:

;; The default :formatters will be used, no :linters will be run:
(formatting-stack.core/format! :linters [])

...And you can also override specific parameters (like max line length from 130 to 80) in a fine-grained manner, as documented in customization_example.clj.

License

Copyright © Nedap

This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0.