1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
|
(define-module (text markup)
:use-module (hnh util)
:use-module (srfi srfi-1)
:use-module (srfi srfi-71)
:use-module (ice-9 match)
:use-module (ice-9 pretty-print)
:use-module (text util)
:use-module (text flow)
:use-module (texinfo string-utils))
;; Takes an HTML-like sxml coded tree, and produces a string with
;; appropriate spacing and ANSI-escapes for different tags.
(define-public (sxml->ansi-text tree)
((parse-tree ontree onleaf) tree))
(define (esc . effect)
(format #f "\x1b[~am"
(if (null? effect)
""
(case (car effect)
[(bold) 1]
[(italic) 3]
[(invert) 7]
[else 4]))))
;; tag := (tag-name [(@ attributes ...)] body ...)
;; alist → tag → tag
(define (add-attributes args)
(match-lambda
[(name ('@ tagargs ...) body ...)
`(,name (@ ,@(assq-limit (assq-merge tagargs args)))
,@body)]
[(name body ...)
`(,name (@ ,@args) ,@body)]
[nonlist nonlist]))
(define (get-attr args key default)
(aif (assoc-ref args key)
(car it) default))
;; NOTE Some tags can be given a `width' attribute. This is however not yet
;; fully supported.
(define* (ontree tag body optional: (args '()))
(case tag
[(*TOP* group block) (string-concatenate
(map (compose sxml->ansi-text (add-attributes args))
body))]
[(header) (sxml->ansi-text `(group (center (@ ,@args) (b ,@body)) (br)))]
[(center) (center-string (string-concatenate (map sxml->ansi-text body))
(get-attr args 'width 70))]
[(p) (string-append (string-join (flow-text (string-concatenate (map sxml->ansi-text body))
width: (get-attr args 'width 70))
"\n")
(if (assoc-ref args 'inline) "" "\n\n")
)]
[(b) (string-append (esc 'bold) (string-concatenate (map sxml->ansi-text body)) (esc))]
[(i em) (string-append (esc 'italic) (string-concatenate (map sxml->ansi-text body)) (esc))]
;; NOOP, but for future use.
[(code) (string-concatenate (map sxml->ansi-text body))]
[(blockquote) (string-concatenate
(map (lambda (line) (sxml->ansi-text `(group (ws (@ (minwidth 4))) ,line (br))))
(flow-text
(string-concatenate (map sxml->ansi-text body))
width: 66)))]
[(ws) (make-string (aif (assoc-ref args 'minwidth)
(car it) 1)
#\space)]
[(br) "\n"]
[(hr) (string-append " " (make-string 60 #\─) " \n")]
[(dl)
(let* ((dts dds (partition (lambda (x) (eq? 'dt (car x))) body))
(dts* (map sxml->ansi-text dts))
(m (if (null? dts*) 0 (apply max (map true-string-length dts*)))))
(string-concatenate
(map (lambda (dt dd)
(let ((dds (string-split dd #\newline)))
(string-concatenate
(map (lambda (left right)
(string-append (true-string-pad left m) " │ " right "\n"))
(cons dt (map (const "") (iota (1- (length dds)))))
dds))))
dts*
(map (compose sxml->ansi-text (add-attributes `((width ,(- 70 m 5)))))
dds))))]
[(dt) (string-concatenate (map (compose sxml->ansi-text (add-attributes args))
body))]
[(dd)
(string-concatenate
(map (compose sxml->ansi-text (add-attributes args))
body))]
[(scheme)
(string-concatenate
(map (lambda (form)
(string-trim-both
(with-output-to-string
(lambda () (pretty-print form width: (aif (assoc-ref args 'width) (car it) 70))))))
body))]
[else (string-append (esc 'bold) "??"
"`"
(esc 'invert)
(string-concatenate (map sxml->ansi-text body))
(esc) "'")]
)
)
(define (onleaf leaf)
(format #f "~a" leaf))
(define (parse-tree tree-callback leaf-callback)
(match-lambda
[(tag ('@ args ...) body ...)
(tree-callback tag body args)]
[(tag body ...)
(tree-callback tag body)
]
[() ""]
[(any ...) (map leaf-callback any)]
[any (leaf-callback any)]))
|