Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 3 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,12 +95,6 @@ As you can see in the screenshot, **formatting-stack** presents linters' outputs
* Else, please add the latest version to your project (or personal [profile](https://github.com/technomancy/leiningen/blob/072dcd62dea0ea46413cf938878e2d31b76357c9/doc/PROFILES.md)).
* If this dependency isn't added, formatting-stack will degrade gracefully, using slightly fewer formatters/linters.

### Component/Integrant integration

**formatting-stack** provides components that you can integrate into your system.

The provided components are fully configurable. See `formatting-stack.core`, `formatting-stack.component`, `formatting-stack.integrant`.

### Reloaded Workflow integration

* If you use the Component component, then `com.stuartsierra.component.repl/reset` will use formatting-stack, applying all its formatters/linters.
Expand All @@ -121,6 +115,8 @@ For that case, you can create some facility (e.g. shortcut, snippet) for the fol

[`formatting-stack.core/format!`](https://github.com/nedap/formatting-stack/blob/0d78f726555db175aa446f4a0a9d2e289cfdd540/src/formatting_stack/core.clj#L49) 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!`](https://github.com/nedap/formatting-stack/blob/5d66e2adffd1696af8b020c56d33d443b299aabd/src/formatting_stack/branch_formatter.clj#L84), [`format-and-lint-project!`](https://github.com/nedap/formatting-stack/blob/5d66e2adffd1696af8b020c56d33d443b299aabd/src/formatting_stack/project_formatter.clj#L84).

## Advanced configuration

If you want to add custom members to the `format!` options (namely: `:formatters`, or `:strategies`, etc), a typical pattern would be:
Expand All @@ -136,10 +132,7 @@ You can also pass `[]` for disabling a particular aspect:
(formatting-stack.core/format! :linters [])
```

If you need something more fine-grained, you are encouraged to copy the contents of the `formatting-stack.defaults` ns to your project, adapting things as needed.
That ns is a deliberately thin and data-only one, with the precise purpose of being forked at no cost.

> We might implement an easier way to configure the stack: [#38](https://github.com/nedap/formatting-stack/issues/38)
...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](https://github.com/nedap/formatting-stack/blob/master/test-resources/customization_example.clj).

## [FAQ](https://github.com/nedap/formatting-stack/wiki/FAQ)

Expand Down
3 changes: 2 additions & 1 deletion src/formatting_stack/formatters/clean_ns.clj
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,8 @@
libspec-whitelist default-libspec-whitelist
refactor-nrepl-opts @default-nrepl-opts
how-to-ns-opts {}}}]
(implement {:refactor-nrepl-opts (deep-merge @default-nrepl-config-opts refactor-nrepl-opts)
(implement {:id ::id
:refactor-nrepl-opts (deep-merge @default-nrepl-config-opts refactor-nrepl-opts)
:how-to-ns-opts (deep-merge formatting-stack.formatters.how-to-ns/default-how-to-ns-opts how-to-ns-opts)
:libspec-whitelist libspec-whitelist
:namespaces-that-should-never-cleaned namespaces-that-should-never-cleaned}
Expand Down
2 changes: 1 addition & 1 deletion src/formatting_stack/formatters/cljfmt.clj
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,5 @@
nil)

(speced/defn new [{:keys [third-party-indent-specs] :as options}]
(implement options
(implement (assoc options :id ::id)
formatter/--format! format!))
5 changes: 3 additions & 2 deletions src/formatting_stack/formatters/how_to_ns.clj
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
nil)

(defn new [{:keys [how-to-ns-options]
:or {how-to-ns-options {}}}]
(implement {:how-to-ns-options (deep-merge default-how-to-ns-opts how-to-ns-options)}
:or {how-to-ns-options {}}}]
(implement {:id ::id
:how-to-ns-options (deep-merge default-how-to-ns-opts how-to-ns-options)}
formatter/--format! format!))
5 changes: 3 additions & 2 deletions src/formatting_stack/formatters/newlines.clj
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
nil)

(speced/defn new [{:keys [^pos-int? expected-newline-count]
:or {expected-newline-count 1}}]
(implement {:expected-newline-count expected-newline-count}
:or {expected-newline-count 1}}]
(implement {:id ::id
:expected-newline-count expected-newline-count}
formatter/--format! format!))
2 changes: 1 addition & 1 deletion src/formatting_stack/formatters/no_extra_blank_lines.clj
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,5 @@
nil)

(defn new []
(implement {}
(implement {:id ::id}
formatter/--format! format!))
5 changes: 3 additions & 2 deletions src/formatting_stack/formatters/trivial_ns_duplicates.clj
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@
nil)

(defn new [{:keys [how-to-ns-opts]
:or {how-to-ns-opts {}}}]
(implement {:how-to-ns-opts (deep-merge formatting-stack.formatters.how-to-ns/default-how-to-ns-opts how-to-ns-opts)}
:or {how-to-ns-opts {}}}]
(implement {:id ::id
:how-to-ns-opts (deep-merge formatting-stack.formatters.how-to-ns/default-how-to-ns-opts how-to-ns-opts)}
formatter/--format! format!))
3 changes: 2 additions & 1 deletion src/formatting_stack/linters/eastwood.clj
Original file line number Diff line number Diff line change
Expand Up @@ -47,5 +47,6 @@

(defn new [{:keys [eastwood-options]
:or {eastwood-options {}}}]
(implement {:options (deep-merge default-eastwood-options eastwood-options)}
(implement {:id ::id
:options (deep-merge default-eastwood-options eastwood-options)}
linter/--lint! lint!))
3 changes: 2 additions & 1 deletion src/formatting_stack/linters/kondo.clj
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@

(defn new [{:keys [kondo-clj-options
kondo-cljs-options]}]
(implement {:kondo-clj-options kondo-clj-options
(implement {:id ::id
:kondo-clj-options kondo-clj-options
:kondo-cljs-options kondo-cljs-options}
protocols.linter/--lint! lint!))
3 changes: 2 additions & 1 deletion src/formatting_stack/linters/line_length.clj
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,6 @@

(defn new [{:keys [max-line-length]
:or {max-line-length 130}}]
(implement {:max-line-length max-line-length}
(implement {:id ::id
:max-line-length max-line-length}
linter/--lint! lint!))
3 changes: 2 additions & 1 deletion src/formatting_stack/linters/loc_per_ns.clj
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,6 @@

(defn new [{:keys [max-lines-per-ns]
:or {max-lines-per-ns 350}}]
(implement {:max-lines-per-ns max-lines-per-ns}
(implement {:id ::id
:max-lines-per-ns max-lines-per-ns}
linter/--lint! lint!))
3 changes: 2 additions & 1 deletion src/formatting_stack/linters/ns_aliases.clj
Original file line number Diff line number Diff line change
Expand Up @@ -90,5 +90,6 @@

(defn new [{:keys [acceptable-aliases-whitelist]
:or {acceptable-aliases-whitelist default-acceptable-aliases-whitelist}}]
(implement {:acceptable-aliases-whitelist acceptable-aliases-whitelist}
(implement {:id ::id
:acceptable-aliases-whitelist acceptable-aliases-whitelist}
linter/--lint! lint!))
2 changes: 1 addition & 1 deletion src/formatting_stack/linters/one_resource_per_ns.clj
Original file line number Diff line number Diff line change
Expand Up @@ -67,5 +67,5 @@
(apply concat)))

(speced/defn new [^map? opts]
(implement opts
(implement (assoc opts :id ::id)
linter/--lint! lint!))
2 changes: 1 addition & 1 deletion src/formatting_stack/processors/cider.clj
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,5 @@
"This processor alters var metadata from third-party libs,
so that runtime-based tooling such as CIDER can work more accurately."
[{:keys [third-party-indent-specs] :as options}]
(implement options
(implement (assoc options :id ::id)
processor/--process! process!))
2 changes: 1 addition & 1 deletion src/formatting_stack/processors/test_runner.clj
Original file line number Diff line number Diff line change
Expand Up @@ -40,5 +40,5 @@
(process! {} filenames)))

(defn new []
(implement {}
(implement {:id ::id}
processor/--process! process!))
3 changes: 2 additions & 1 deletion src/formatting_stack/protocols/formatter.clj
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@

Normally it's a wrapper around a formatting library, with extra configuration, performance improvements, etc."

(^nil? format! [this, ^::protocols.spec/filenames filenames]
(^nil? format! [^::protocols.spec/member this
^::protocols.spec/filenames filenames]
"Formats `filenames` according to a formatter of your choice."))
3 changes: 2 additions & 1 deletion src/formatting_stack/protocols/linter.clj
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@

Normally it's a wrapper around a linting library, with extra configuration, performance improvements, etc."

(^::protocols.spec/reports lint! [this, ^::protocols.spec/filenames filenames]
(^::protocols.spec/reports lint! [^::protocols.spec/member this
^::protocols.spec/filenames filenames]
"Lints `filenames` according to a linter of your choice: e.g. Eastwood, or Kibit, lein-dependency-check, etc."))
3 changes: 2 additions & 1 deletion src/formatting_stack/protocols/processor.clj
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
(speced/defprotocol Processor
"Any file-processing component that isn't a formatter or a linter."

(^nil? process! [this, ^::protocols.spec/filenames filenames]
(^nil? process! [^::protocols.spec/member this
^::protocols.spec/filenames filenames]
"Performs a compilation according to a processor of your choice: e.g. the ClojureScript processor, or Garden, Stefon, etc.
You are free to ignore `filenames`, compiling the whole project instead."))
19 changes: 19 additions & 0 deletions src/formatting_stack/protocols/spec.clj
Original file line number Diff line number Diff line change
@@ -1,8 +1,27 @@
(ns formatting-stack.protocols.spec
(:require
[clojure.spec.alpha :as spec]
[nedap.speced.def :as speced]
[nedap.utils.spec.api :refer [check!]]
[nedap.utils.spec.predicates :refer [present-string?]]))

(speced/def-with-doc :formatting-stack.protocols.spec.member/id
"Members (formatters, linters, etc) identify themselves,
so that final users can locate them and configure them."
keyword?)

(speced/def-with-doc ::member
"A 'member' of the stack that does something useful: a formatter, linter or processor.

'Strategies' and 'Reporters' are not members - instead they help members accomplish their purpose."
(fn [x]
(if-not (map? x)
;; we are facing a `reify`, which means that formatting-stack is being customized
;; In those cases, an :id is practically useless (since the point of :id is overriding f-s), so no validation needed:
true
(check! (spec/keys :req-un [:formatting-stack.protocols.spec.member/id])
x))))

(spec/def ::filename present-string?)

(spec/def ::filenames (spec/coll-of ::filename))
Expand Down
30 changes: 19 additions & 11 deletions test-resources/component_repl.clj
Original file line number Diff line number Diff line change
@@ -1,21 +1,29 @@
(ns component-repl
"Documents a working setup for Component.
"Documents a working setup for Component, as a runnable example.

Not a part of the test suite."
Not a part of the test suite.

See also:

* https://github.com/nedap/formatting-stack/blob/master/README.md
* https://github.com/nedap/formatting-stack/wiki/FAQ
* The `customization-example` sibling namespace."
(:require
[com.stuartsierra.component :as component]
[com.stuartsierra.component.repl :as component.repl]
[formatting-stack.component]))
[formatting-stack.component]
[formatting-stack.defaults]))

(def sample-linters
[(reify formatting-stack.protocols.linter/Linter
(--lint! [this filenames]
[{:source ::my-linter
:level :warning
:column 40
:line 6
:msg "Hello, I am a sample linter!"
:filename "path.clj"}]))])
(conj formatting-stack.defaults/default-linters
(reify formatting-stack.protocols.linter/Linter
(--lint! [this filenames]
[{:source ::my-linter
:level :warning
:column 40
:line 6
:msg "Hello, I am a sample linter!"
:filename "path.clj"}]))))

(defn init [_]
(component/system-map :formatting-stack (formatting-stack.component/new {:linters sample-linters})))
Expand Down
54 changes: 54 additions & 0 deletions test-resources/customization_example.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
(ns customization-example
"Documents how users can customize formatting-stack, as a runnable example.

Not a part of the test suite.

See also:

* https://github.com/nedap/formatting-stack/blob/master/README.md
* https://github.com/nedap/formatting-stack/wiki/FAQ"
(:require
[formatting-stack.core]
[formatting-stack.defaults]
[formatting-stack.linters.kondo :as kondo]
[formatting-stack.linters.line-length :as line-length]
[formatting-stack.linters.ns-aliases :as ns-aliases]))

;; You an implement your own linters:
(def custom-linters
[(reify formatting-stack.protocols.linter/Linter
(--lint! [this filenames]
[{:source ::my-linter
:level :warning
:column 40
:line 6
:msg "Hello, I am a sample linter!"
:filename "path.clj"}]))])

;; You can tweak the default linters' configuration:
(def tweaked-linters
(->> formatting-stack.defaults/default-linters
(keep (fn [{:keys [id] :as linter}]
;; all formatters and linters have an `:id`.
(case id
;; change :max-line-length from 130 to 80:
::line-length/id (assoc linter :max-line-length 80)

;; remove an undesired linter:
::ns-aliases/id nil

;; override some kondo defaults. They will be deep-merged against formatting-stack's kondo config:
::kondo/id (assoc linter
:kondo-clj-options {:linters {:cond-else {:level :warning}}}
;; remember there are different options, for clj and cljs.
:kondo-cljs-options {:linters {:duplicate-require {:level :warning}}})
linter)))
(vec)))

(def all-linters
(into custom-linters tweaked-linters))

(comment
(formatting-stack.core/format! :linters all-linters
:formatters [] ;; disable all formatters (as an example of how to do that)
:in-background? false))
28 changes: 18 additions & 10 deletions test-resources/integrant_repl.clj
Original file line number Diff line number Diff line change
@@ -1,22 +1,30 @@
(ns integrant-repl
"Documents a working setup for Integrant.
"Documents a working setup for Integrant, as a runnable example.

Not a part of the test suite."
Not a part of the test suite.

See also:

* https://github.com/nedap/formatting-stack/blob/master/README.md
* https://github.com/nedap/formatting-stack/wiki/FAQ
* The `customization-example` sibling namespace."
(:require
[clojure.spec.alpha :as spec]
[formatting-stack.defaults]
[formatting-stack.integrant]
[formatting-stack.protocols.spec :as protocols.spec]
[integrant.repl]))

(def sample-linters
[(reify formatting-stack.protocols.linter/Linter
(--lint! [this filenames]
[{:source ::my-linter
:level :warning
:column 40
:line 6
:msg "Hello, I am a sample linter!"
:filename "path.clj"}]))])
(conj formatting-stack.defaults/default-linters
(reify formatting-stack.protocols.linter/Linter
(--lint! [this filenames]
[{:source ::my-linter
:level :warning
:column 40
:line 6
:msg "Hello, I am a sample linter!"
:filename "path.clj"}]))))

(integrant.repl/set-prep! (constantly {:formatting-stack.integrant/component {:linters sample-linters}}))

Expand Down