Skip to content

Commit

Permalink
feat: pretty printing without color (#16)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
Macroz authored Oct 12, 2022
1 parent 884f7ed commit 19c6c08
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 4 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@ Options:
-i, --in FORMAT yaml Input format: csv, edn, json, lines, msgpack, text, transit, yaml
-o, --out FORMAT edn Output format: csv, edn, json, lines, msgpack, text, transit, yaml
-p, --[no-]pretty Pretty print output - default is true
--color COLOR auto When pretty printing, whether to use colors: auto, off, on - default is auto
-c Same as --color=on
-C Same as --color=off
-k, --key-fn FN keyword Function used to transform keys - currently only supported for JSON and CSV
--yaml-unsafe Enables unsafe mode in clj-yaml / SnakeYAML
--[no-]yaml-keywords Turn map keys into keywords in clj-yaml - default is true
Expand Down
6 changes: 4 additions & 2 deletions src/cq/formats.clj
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,13 @@
(edn/read (PushbackReader. (io/reader in)))))

(defn ->edn-writer
[{:keys [pretty]}]
[{:keys [pretty color]}]
(if pretty
(fn [x out]
(binding [*out* (io/writer out)]
(puget/cprint x)))
(if color
(puget/cprint x)
(puget/pprint x))))
(fn [x out]
(binding [*out* (io/writer out)]
(pr x)
Expand Down
23 changes: 22 additions & 1 deletion src/cq/main.clj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@

(require 'cq.readers)

(def colors #{:auto :on :off})

(def colors-str (str/join ", " (sort (map name colors))))

(def formats (set (keys fmt/formats)))

(def formats-str (str/join ", " (sort formats)))
Expand All @@ -24,6 +28,16 @@
:validate [formats]]
["-p" "--[no-]pretty" "Pretty print output - default is true"
:default true]
[nil "--color COLOR" (str "When pretty printing, whether to use colors: " colors-str " - default is auto")
:default "auto"
:parse-fn keyword
:validate [colors]]
["-c" nil "Same as --color=on"
:id :color
:assoc-fn (fn [m _ _] (assoc m :color :on))]
["-C" nil "Same as --color=off"
:id :color
:assoc-fn (fn [m _ _] (assoc m :color :off))]
["-k" "--key-fn FN" "Function used to transform keys - currently only supported for JSON and CSV"
:default "keyword"]
[nil "--yaml-unsafe" "Enables unsafe mode in clj-yaml / SnakeYAML"]
Expand Down Expand Up @@ -96,11 +110,18 @@
(def ^:dynamic *stdin* System/in)
(def ^:dynamic *stdout* System/out)

(defn- handle-auto-options [opts]
(update opts :color #(case %
:auto true ; would be nice to detect
:on true
false)))

(defn main
[args]
(let [{:keys [arguments exit-message ok?]
{:keys [in out] :as opts} :options}
(validate-args args)]
(validate-args args)
opts (handle-auto-options opts)]
(if exit-message
(exit (if ok? 0 1) exit-message)
(let [expressions (args->exprs arguments)
Expand Down
20 changes: 19 additions & 1 deletion test/cq/formats_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,25 @@
(is (= {:a {:b [1 2 3]}} (test-reader-str sut/->edn-reader nil "{:a {:b [1 2 3]}}"))))

(testing "writer"
(is (= "{:a {:b [1 2 3]}}\n" (test-writer-str sut/->edn-writer nil {:a {:b [1 2 3]}})))))
(is (= "{:a {:b [1 2 3]}}\n" (test-writer-str sut/->edn-writer nil {:a {:b [1 2 3]}})))

(testing "pretty"
(is (= "{:a {:b [1 2 3],\n :c [2 3 {:something :long}],\n :d [1 2 {:something :even/longer}]}}\n"
(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}]}})))

(testing "color"
(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"
(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}]}})))))))

(comment
;; simple utility for printing an ANSI escaped sequence for a test constant
;; ANSI uses the ESC character for escape codes and it is lost in printing
(let [x {:a {:b [1 2 3] :c [2 3 {:something :long}] :d [1 2 {:something :even/longer}]}}
s (test-writer-str sut/->edn-writer {:pretty true :color true} x)]
(str/join (for [c s]
(if (= 27 (int c))
"ESC" ; replace with \u001B and you get a test constant
c)))))

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

0 comments on commit 19c6c08

Please sign in to comment.