Skip to content

Commit 19c6c08

Browse files
authored
feat: pretty printing without color (#16)
Introduce a new flag `--color` that decides whether colors are printed while pretty-printing is on. Valid values are `on`, `off` and `auto`, which is the default. In the future, there is the possibility of autodetecting from TTY settings, but at the time of writing `auto` means `on`. The flags `-c` and `-C` can also be used to toggle color on/off respectively. NB: Currently, only applies to EDN printing. Additionally tests the pretty-printing with multiple line case, and ANSI color escape sequences.
1 parent 884f7ed commit 19c6c08

File tree

4 files changed

+48
-4
lines changed

4 files changed

+48
-4
lines changed

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,9 @@ Options:
7676
-i, --in FORMAT yaml Input format: csv, edn, json, lines, msgpack, text, transit, yaml
7777
-o, --out FORMAT edn Output format: csv, edn, json, lines, msgpack, text, transit, yaml
7878
-p, --[no-]pretty Pretty print output - default is true
79+
--color COLOR auto When pretty printing, whether to use colors: auto, off, on - default is auto
80+
-c Same as --color=on
81+
-C Same as --color=off
7982
-k, --key-fn FN keyword Function used to transform keys - currently only supported for JSON and CSV
8083
--yaml-unsafe Enables unsafe mode in clj-yaml / SnakeYAML
8184
--[no-]yaml-keywords Turn map keys into keywords in clj-yaml - default is true

src/cq/formats.clj

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,13 @@
3535
(edn/read (PushbackReader. (io/reader in)))))
3636

3737
(defn ->edn-writer
38-
[{:keys [pretty]}]
38+
[{:keys [pretty color]}]
3939
(if pretty
4040
(fn [x out]
4141
(binding [*out* (io/writer out)]
42-
(puget/cprint x)))
42+
(if color
43+
(puget/cprint x)
44+
(puget/pprint x))))
4345
(fn [x out]
4446
(binding [*out* (io/writer out)]
4547
(pr x)

src/cq/main.clj

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77

88
(require 'cq.readers)
99

10+
(def colors #{:auto :on :off})
11+
12+
(def colors-str (str/join ", " (sort (map name colors))))
13+
1014
(def formats (set (keys fmt/formats)))
1115

1216
(def formats-str (str/join ", " (sort formats)))
@@ -24,6 +28,16 @@
2428
:validate [formats]]
2529
["-p" "--[no-]pretty" "Pretty print output - default is true"
2630
:default true]
31+
[nil "--color COLOR" (str "When pretty printing, whether to use colors: " colors-str " - default is auto")
32+
:default "auto"
33+
:parse-fn keyword
34+
:validate [colors]]
35+
["-c" nil "Same as --color=on"
36+
:id :color
37+
:assoc-fn (fn [m _ _] (assoc m :color :on))]
38+
["-C" nil "Same as --color=off"
39+
:id :color
40+
:assoc-fn (fn [m _ _] (assoc m :color :off))]
2741
["-k" "--key-fn FN" "Function used to transform keys - currently only supported for JSON and CSV"
2842
:default "keyword"]
2943
[nil "--yaml-unsafe" "Enables unsafe mode in clj-yaml / SnakeYAML"]
@@ -96,11 +110,18 @@
96110
(def ^:dynamic *stdin* System/in)
97111
(def ^:dynamic *stdout* System/out)
98112

113+
(defn- handle-auto-options [opts]
114+
(update opts :color #(case %
115+
:auto true ; would be nice to detect
116+
:on true
117+
false)))
118+
99119
(defn main
100120
[args]
101121
(let [{:keys [arguments exit-message ok?]
102122
{:keys [in out] :as opts} :options}
103-
(validate-args args)]
123+
(validate-args args)
124+
opts (handle-auto-options opts)]
104125
(if exit-message
105126
(exit (if ok? 0 1) exit-message)
106127
(let [expressions (args->exprs arguments)

test/cq/formats_test.clj

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,25 @@
6060
(is (= {:a {:b [1 2 3]}} (test-reader-str sut/->edn-reader nil "{:a {:b [1 2 3]}}"))))
6161

6262
(testing "writer"
63-
(is (= "{:a {:b [1 2 3]}}\n" (test-writer-str sut/->edn-writer nil {:a {:b [1 2 3]}})))))
63+
(is (= "{:a {:b [1 2 3]}}\n" (test-writer-str sut/->edn-writer nil {:a {:b [1 2 3]}})))
64+
65+
(testing "pretty"
66+
(is (= "{:a {:b [1 2 3],\n :c [2 3 {:something :long}],\n :d [1 2 {:something :even/longer}]}}\n"
67+
(test-writer-str sut/->edn-writer {:pretty true :color false} {:a {:b [1 2 3] :c [2 3 {:something :long}] :d [1 2 {:something :even/longer}]}})))
68+
69+
(testing "color"
70+
(is (= "\u001B[1;31m{\u001B[0m\u001B[1;33m:a\u001B[0m \u001B[1;31m{\u001B[0m\u001B[1;33m:b\u001B[0m \u001B[1;31m[\u001B[0m\u001B[36m1\u001B[0m \u001B[36m2\u001B[0m \u001B[36m3\u001B[0m\u001B[1;31m]\u001B[0m,\n \u001B[1;33m:c\u001B[0m \u001B[1;31m[\u001B[0m\u001B[36m2\u001B[0m \u001B[36m3\u001B[0m \u001B[1;31m{\u001B[0m\u001B[1;33m:something\u001B[0m \u001B[1;33m:long\u001B[0m\u001B[1;31m}\u001B[0m\u001B[1;31m]\u001B[0m,\n \u001B[1;33m:d\u001B[0m \u001B[1;31m[\u001B[0m\u001B[36m1\u001B[0m \u001B[36m2\u001B[0m \u001B[1;31m{\u001B[0m\u001B[1;33m:something\u001B[0m \u001B[1;33m:even/longer\u001B[0m\u001B[1;31m}\u001B[0m\u001B[1;31m]\u001B[0m\u001B[1;31m}\u001B[0m\u001B[1;31m}\u001B[0m\n"
71+
(test-writer-str sut/->edn-writer {:pretty true :color true} {:a {:b [1 2 3] :c [2 3 {:something :long}] :d [1 2 {:something :even/longer}]}})))))))
72+
73+
(comment
74+
;; simple utility for printing an ANSI escaped sequence for a test constant
75+
;; ANSI uses the ESC character for escape codes and it is lost in printing
76+
(let [x {:a {:b [1 2 3] :c [2 3 {:something :long}] :d [1 2 {:something :even/longer}]}}
77+
s (test-writer-str sut/->edn-writer {:pretty true :color true} x)]
78+
(str/join (for [c s]
79+
(if (= 27 (int c))
80+
"ESC" ; replace with \u001B and you get a test constant
81+
c)))))
6482

6583
(defn resource->bytes [file]
6684
(with-open [xin (io/input-stream (io/resource file))

0 commit comments

Comments
 (0)