Skip to content

Commit a87a3c5

Browse files
authored
Merge pull request #121 from clj-commons/hls/vector-fonts-120
Allow a vector of keywords to define font characteristics
2 parents 4a3b207 + df441f1 commit a87a3c5

File tree

5 files changed

+81
-38
lines changed

5 files changed

+81
-38
lines changed

CHANGES.md

+7
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
## 2.6.0 - UNRELEASED
2+
3+
- Font declaration in `compose` can now be a vector of individual terms, rather than a single keyword; e.g. `[:bold :red]`
4+
rather than `:bold.red`.
5+
6+
[Closed Issues](https://github.com/clj-commons/pretty/milestone/49?closed=1)
7+
18
## 2.5.1 - 12 Apr 2024
29

310
Minor bug fixes.

deps.edn

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
{:extra-paths ["test"]
88
:extra-deps {criterium/criterium {:mvn/version "0.4.6"}
99
org.clojure/core.async {:mvn/version "1.6.681"}
10+
nubank/matcher-combinators {:mvn/version "3.9.1"}
1011
io.github.cognitect-labs/test-runner {:git/tag "v0.5.1"
1112
:git/sha "dfb30dd"}}
1213
:jvm-opts ["-Dclj-commons.ansi.enabled=true"]

src/clj_commons/ansi.clj

+48-32
Original file line numberDiff line numberDiff line change
@@ -56,23 +56,23 @@
5656

5757
(def ^:private font-terms
5858
(reduce merge
59-
{:bold [:bold "1"]
60-
:plain [:bold "22"]
61-
:faint [:bold "2"]
59+
{:bold [:bold "1"]
60+
:plain [:bold "22"]
61+
:faint [:bold "2"]
6262

63-
:italic [:italic "3"]
64-
:roman [:italic "23"]
63+
:italic [:italic "3"]
64+
:roman [:italic "23"]
6565

66-
:inverse [:inverse "7"]
67-
:normal [:inverse "27"]
66+
:inverse [:inverse "7"]
67+
:normal [:inverse "27"]
6868

69-
:underlined [:underlined "4"]
69+
:underlined [:underlined "4"]
7070
:not-underlined [:underlined "24"]}
7171
(map-indexed
7272
(fn [index color-name]
73-
{(keyword color-name) [:foreground (str (+ 30 index))]
74-
(keyword (str "bright-" color-name)) [:foreground (str (+ 90 index))]
75-
(keyword (str color-name "-bg")) [:background (str (+ 40 index))]
73+
{(keyword color-name) [:foreground (str (+ 30 index))]
74+
(keyword (str "bright-" color-name)) [:foreground (str (+ 90 index))]
75+
(keyword (str color-name "-bg")) [:background (str (+ 40 index))]
7676
(keyword (str "bright-" color-name "-bg")) [:background (str (+ 100 index))]})
7777
["black" "red" "green" "yellow" "blue" "magenta" "cyan" "white"])))
7878

@@ -94,19 +94,28 @@
9494
(assert (simple-keyword? font-def) "expected a simple keyword to define the font characteristics")
9595
(mapv keyword (str/split (name font-def) #"\.")))
9696

97-
(def ^:private split-font-def (memoize split-font-def*))
97+
(def ^:private memoized-split-font-def* (memoize split-font-def*))
98+
99+
(defn ^:private split-font-def
100+
[font-def]
101+
(if (vector? font-def)
102+
font-def
103+
(memoized-split-font-def* font-def)))
98104

99105
(defn- update-font-data-from-font-def
100106
[font-data font-def]
101107
(if (some? font-def)
102108
(let [ks (split-font-def font-def)
103109
f (fn [font-data term]
104-
(let [[font-k font-value] (or (get font-terms term)
105-
(throw (ex-info (str "unexpected font term: " term)
106-
{:font-term term
107-
:font-def font-def
108-
:available-terms (->> font-terms keys sort vec)})))]
109-
(assoc! font-data font-k font-value)))]
110+
;; nils are allowed in the vector form of a font def
111+
(if (nil? term)
112+
font-data
113+
(let [[font-k font-value] (or (get font-terms term)
114+
(throw (ex-info (str "unexpected font term: " term)
115+
{:font-term term
116+
:font-def font-def
117+
:available-terms (->> font-terms keys sort vec)})))]
118+
(assoc! font-data font-k font-value))))]
110119
(persistent! (reduce f (transient font-data) ks)))
111120
font-data))
112121

@@ -116,7 +125,10 @@
116125
(nil? value)
117126
nil
118127

119-
(keyword? value)
128+
;; It would be tempting to split the keyword here, but that would obscure an error if the keyword
129+
;; contains a substring that isn't an expected font term.
130+
(or (keyword? value)
131+
(vector? value))
120132
{:font value}
121133

122134
(map? value)
@@ -173,11 +185,11 @@
173185
;; If at or over desired width, don't need to pad
174186
(if (<= width actual-width)
175187
{:width actual-width
176-
:span inputs'})
188+
:span inputs'})
177189
;; Add the padding in the desired position(s); this ensures that the logic that generates
178190
;; ANSI escape codes occurs correctly, with the added spaces getting the font for this span.
179191
{:width width
180-
:span (apply-padding inputs' pad width actual-width)}))
192+
:span (apply-padding inputs' pad width actual-width)}))
181193

182194

183195
(defn- normalize-markup
@@ -252,15 +264,15 @@
252264
[inputs]
253265
(let [initial-font {:foreground "39"
254266
:background "49"
255-
:bold "22"
256-
:italic "23"
257-
:inverse "27"
267+
:bold "22"
268+
:italic "23"
269+
:inverse "27"
258270
:underlined "24"}
259271
buffer (StringBuilder. 100)
260-
{:keys [dirty?]} (collect-markup {:stack []
261-
:active initial-font
272+
{:keys [dirty?]} (collect-markup {:stack []
273+
:active initial-font
262274
:current initial-font
263-
:buffer buffer}
275+
:buffer buffer}
264276
inputs)]
265277
(when dirty?
266278
(.append buffer reset-font))
@@ -310,6 +322,10 @@
310322
Font defs apply on top of the font def of the enclosing span, and the outer span's font def
311323
is restored at the end of the inner span, e.g. `[:red \" RED \" [:bold \"RED/BOLD\"] \" RED \"]`.
312324
325+
Alternately, a font def may be a vector of individual keyword, e.g., `[[:bold :red] ...]` rather than
326+
`[:bold.red ...]`. This works better when the exact font characteristics are determined
327+
dynamically.
328+
313329
A font def may also be nil, to indicate no change in font.
314330
315331
`compose` presumes that on entry the current font is plain (default foreground and background, not bold,
@@ -323,11 +339,11 @@
323339
324340
The span's font declaration may also be a map with the following keys:
325341
326-
Key | Type | Description
327-
--- |--- |---
328-
:font | keyword | The font declaration
329-
:width | number | The visual width of the span
330-
:pad | keyword | Where to pad the span, :left, :right, or :both; default is :left
342+
Key | Type | Description
343+
--- |--- |---
344+
:font | keyword or vector of keywords | The font declaration
345+
:width | number | The desired width of the span
346+
:pad | :left, :right, :both | Where to pad the span if :width specified, default is :left
331347
332348
The map form of the font declaration is typically only used when a span width is specified.
333349
The span will be padded with spaces to ensure that it is the specified width. `compose` tracks the number

test/clj_commons/ansi_test.clj

+23-5
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@
5959

6060
;; Check for skipping nils and blank strings, and not emitting the reset if no font
6161
;; changes occurred.
62-
["Prefix--" [:bold nil ""] "--Suffix"]
62+
["Prefix--" [:bold nil ""] "--Suffix"]
6363
"Prefix----Suffix"
6464

6565
;; A bug caused blank strings to be omitted, this checks for the fix:
@@ -188,18 +188,36 @@
188188
:white-bg
189189
:yellow
190190
:yellow-bg]}
191-
(ex-data e)))))
191+
(ex-data e)))))
192+
193+
(deftest unrecognized-vector-font-modifier
194+
(when-let [e (is (thrown? Throwable (compose [[:what :is :this?] "Fail!"])))]
195+
(is (= "unexpected font term: :what" (ex-message e)))
196+
(is (match? {:font-term :what
197+
:font-def [:what :is :this?]}
198+
(ex-data e)))))
192199

193200
(deftest pad-both-even-padding
194201
(is (= "| XX |"
195202
; .... ....
196203
(compose "|" [{:width 10
197-
:pad :both}
204+
:pad :both}
198205
"XX"] "|"))))
199206

200207
(deftest pad-both-odd-padding
201208
(is (= "| X |"
202209
; ..... ....
203210
(compose "|" [{:width 10
204-
:pad :both}
205-
"X"] "|"))))
211+
:pad :both}
212+
"X"] "|"))))
213+
214+
(deftest use-of-vector-font-defs
215+
(doseq [[font-kw font-vector] [[:red.green-bg [:red :green-bg]]
216+
[:blue.italic [:italic :blue]]
217+
[:green.underlined [:green nil :underlined]]]]
218+
(is (= (compose [font-kw "some text"])
219+
(compose [font-vector "some text"])))
220+
221+
222+
(is (= (compose [{:font font-kw} "text"])
223+
(compose [{:font font-vector} "text"])))))

test/user.clj

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1-
(ns user)
1+
(ns user
2+
(:require matcher-combinators.clj-test))
23

34
(set! *warn-on-reflection* true)

0 commit comments

Comments
 (0)