-
Notifications
You must be signed in to change notification settings - Fork 120
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add option to sort ns reference forms
The option results in cljfmt sorting the libspecs inside :require, :require-macros, :use, and :import forms. Whitespace and newlines are preserved for most cases, in particular whether the first libspec element follows :require (or one of the others) or moves to the next line. Comments above and behind a libspec are considered part of that libspec, and they'll move to the new position of the libspec. This means libspecs commented out won't sorted, e.g. [c] #_[b] [a] will sort to #_[b] [a] [c] and similar with ; comments, because it is treated as a comment for [a]. This probably is acceptable, as it is an edge case, and there shouldn't be an expectation that the comment is treated as a libspec.
- Loading branch information
Showing
5 changed files
with
267 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
(ns cljfmt.namespace | ||
(:require [rewrite-clj.node :as n] | ||
[rewrite-clj.zip :as z])) | ||
|
||
(def reference-keyword? | ||
#{:require | ||
:require-macros | ||
:use | ||
:import}) | ||
|
||
(def ^:private libspec? | ||
(comp not | ||
#{:comment | ||
:uneval | ||
:newline | ||
:whitespace | ||
:comma} | ||
n/tag)) | ||
|
||
(defn- contain-newline? [elements] | ||
(->> elements | ||
(apply concat) | ||
(some (comp #{:newline | ||
:comment} | ||
n/tag)))) | ||
|
||
(defn partition-children | ||
"Partition children of a reference form, considering one element to look like: | ||
(<whitespace>|<comment>)*<libspec>(<whitespace>|<comment>)* | ||
This ensures that comments above a libspec and comments behind it will be grouped | ||
and moved together with it." | ||
[zloc] | ||
(loop [[node & remaining] (-> zloc | ||
z/node | ||
n/children) | ||
current-element [] | ||
result []] | ||
(let [t (some-> node n/tag)] | ||
(cond | ||
(nil? node) (if (seq current-element) | ||
(conj result (conj | ||
current-element | ||
(if (contain-newline? result) | ||
(n/newlines 1) | ||
(n/whitespace-node " ")))) | ||
result) | ||
|
||
(#{:whitespace | ||
:comma | ||
:uneval} t) (recur remaining | ||
(conj current-element node) | ||
result) | ||
|
||
(#{:comment | ||
:newline} t) (if (some libspec? current-element) | ||
(recur remaining | ||
[] | ||
(conj result (conj current-element node))) | ||
(recur remaining | ||
(conj current-element node) | ||
result)) | ||
|
||
; otherwise it'll be a libspec node, which should be added if it is the first | ||
; in this element, otherwise close the current element and start a new one; an example | ||
; for this is "[a] b" without a newline, which should result in elements "[a]" and "b" | ||
:else (if (some libspec? current-element) | ||
(recur remaining | ||
[node] | ||
(conj result current-element)) | ||
(recur remaining | ||
(conj current-element node) | ||
result)))))) | ||
|
||
(defn element-sort-key | ||
"The desired order is as follows: | ||
( ; first the keywords, tho only one is expected | ||
:require | ||
; then reader macros | ||
#?(:clj [clojure.java.io :as io]) | ||
; then string libspecs ordered lexicographically | ||
\"x-node-dependency\" | ||
\"y-node-dependency\" | ||
; then any others, ordered lexicographically by the namespace, | ||
; but it shouldn't matter whether there is an alias, refers, a vector, or a list | ||
[a] | ||
[b.c.d :as d] | ||
c | ||
[d :refer [some-function]])" | ||
[element] | ||
(let [relevant (first (filter libspec? element)) | ||
t (some-> relevant n/tag)] | ||
(cond | ||
(#{:reader-macro} t) (let [value (n/sexpr relevant)] | ||
[1 (pr-str value)]) | ||
(#{:token} t) (let [value (n/sexpr relevant)] | ||
(cond | ||
(reference-keyword? value) [0 value] | ||
(string? value) [2 value] | ||
(symbol? value) [3 (name value)] | ||
:else [4 (str value)])) | ||
(#{:meta} t) (some-> relevant | ||
n/children | ||
;; skip over the metadata itself | ||
rest | ||
element-sort-key) | ||
(#{:vector | ||
:list} t) (element-sort-key (n/children relevant)) | ||
|
||
:else [10000 nil]))) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters