@@ -50,6 +50,7 @@ show(io::IO, ::MIME"text/plain", s::Splat) = show(io, s)
50
50
51
51
const possible_ansi_regex = r" \x 1B(?:[@-Z\\ -_\[ ])"
52
52
const start_ansi_regex = r" \G (?:[@-Z\\ -_]|\[ [0-?]*[ -/]*[@-~])"
53
+ const next_ansi_regexes = r" .(?:\x 1B(?:[@-Z\\ -_]|\[ [0-?]*[ -/]*[@-~]))+"
53
54
54
55
function _find_lastidx (str, width, chars, truncwidth, start)
55
56
idx = start
@@ -76,7 +77,7 @@ function _find_lastidx(str, width, chars, truncwidth, start)
76
77
if ! isnothing (m)
77
78
firstmatch == 0 && (firstmatch = lastidx)
78
79
noansi = m. match == " [0m"
79
- s = sizeof (m. match)
80
+ s = ncodeunits (m. match)
80
81
idx += s
81
82
lastidx = idx - 1 # the last character of an ansi delimiter is always of size 1.
82
83
continue
@@ -85,27 +86,48 @@ function _find_lastidx(str, width, chars, truncwidth, start)
85
86
truncidx == 0 && wid > (width - truncwidth) && (truncidx = last_lastidx)
86
87
stop = (wid >= width || c in chars)
87
88
end
88
- return lastidx, truncidx, noansi || (lastidx < lastindex (str) && truncidx < firstmatch)
89
+ return lastidx, truncidx, noansi || (lastidx < lastindex (str) && truncidx < firstmatch), wid
89
90
end
90
91
91
- function _truncate_at_width_or_chars (ignore_ansi:: Bool , str, width, chars= " " , truncmark= " …" )
92
+ function _truncate_at_width_or_chars (ignore_ansi:: Bool , str, width, rpad = false , chars= " \r\n " , truncmark= " …" )
92
93
truncwidth = textwidth (truncmark)
93
- (width <= 0 || width < truncwidth) && return " "
94
+ (width <= 0 || width < truncwidth) && return rpad ? ' ' ^ width : " "
94
95
# possible_substring is a subset of str at least as large as the returned string
95
96
possible_substring = SubString (str, 1 , thisind (str, min (ncodeunits (str), width+ 1 )))
96
97
m = ignore_ansi ? match (possible_ansi_regex, possible_substring) : nothing
97
98
start_offset = isnothing (m) ? thisind (str, min (ncodeunits (str), width- truncwidth)) : m. offset
98
- lastidx, truncidx, noansi = _find_lastidx (str, width, chars, truncwidth, start_offset)
99
- lastidx == 0 && return " "
99
+ lastidx, truncidx, noansi, wid = _find_lastidx (str, width, chars, truncwidth, start_offset)
100
+ lastidx == 0 && return rpad ? ' ' ^ width : " "
100
101
str[lastidx] in chars && (lastidx = prevind (str, lastidx))
101
102
truncidx == 0 && (truncidx = lastidx)
103
+ endansi = noansi ? " " : " \0 33[0m"
104
+ pad = rpad ? repeat (' ' , max (0 , width- wid)) : " "
102
105
if lastidx < lastindex (str)
103
- return string (SubString (str, 1 , truncidx), noansi ? " " : " \0 33[0m " , truncmark)
106
+ return string (SubString (str, 1 , truncidx), endansi , truncmark, pad )
104
107
else
105
- return noansi ? String (str) : string (str, " \0 33[0m " )
108
+ return string (str, endansi, pad )
106
109
end
107
110
end
108
111
112
+ function _width_excluding_color (hascolor, str)
113
+ (! hascolor || length (str) < 2 ) && return textwidth (str)
114
+ i = 1
115
+ n = lastindex (str)
116
+ while i ≤ n && str[i] == ' \0 33'
117
+ m = match (start_ansi_regex, str, i+ 1 )
118
+ m isa RegexMatch || break
119
+ i = m. offset + ncodeunits (m. match)
120
+ end
121
+ m = match (next_ansi_regexes, str, i)
122
+ wid = 0
123
+ while m isa RegexMatch
124
+ wid += textwidth (SubString (str, i, m. offset))
125
+ i = m. offset + ncodeunits (m. match)
126
+ m = match (next_ansi_regexes, str, i)
127
+ end
128
+ return wid + textwidth (SubString (str, i, n))
129
+ end
130
+
109
131
function show (io:: IO , :: MIME"text/plain" , iter:: Union{KeySet,ValueIterator} )
110
132
isempty (iter) && get (io, :compact , false ) && return show (io, iter)
111
133
summary (io, iter)
@@ -129,7 +151,7 @@ function show(io::IO, ::MIME"text/plain", iter::Union{KeySet,ValueIterator})
129
151
130
152
if limit
131
153
str = sprint (show, v, context= io, sizehint= 0 )
132
- str = _truncate_at_width_or_chars (get (io, :color , false ), str, cols, " \r\n " )
154
+ str = _truncate_at_width_or_chars (get (io, :color , false ), str, cols)
133
155
print (io, str)
134
156
else
135
157
show (io, v)
@@ -161,19 +183,20 @@ function show(io::IO, ::MIME"text/plain", t::AbstractDict{K,V}) where {K,V}
161
183
rows -= 1 # Subtract the summary
162
184
163
185
# determine max key width to align the output, caching the strings
186
+ hascolor = get (recur_io, :color , false )
164
187
ks = Vector {String} (undef, min (rows, length (t)))
165
188
vs = Vector {String} (undef, min (rows, length (t)))
166
- keylen = 0
167
- vallen = 0
189
+ keywidth = 0
190
+ valwidth = 0
168
191
for (i, (k, v)) in enumerate (t)
169
192
i > rows && break
170
193
ks[i] = sprint (show, k, context= recur_io_k, sizehint= 0 )
171
194
vs[i] = sprint (show, v, context= recur_io_v, sizehint= 0 )
172
- keylen = clamp (length ( ks[i]), keylen , cols)
173
- vallen = clamp (length ( vs[i]), vallen , cols)
195
+ keywidth = clamp (_width_excluding_color (hascolor, ks[i]), keywidth , cols)
196
+ valwidth = clamp (_width_excluding_color (hascolor, vs[i]), valwidth , cols)
174
197
end
175
- if keylen > max (div (cols, 2 ), cols - vallen )
176
- keylen = max (cld (cols, 3 ), cols - vallen )
198
+ if keywidth > max (div (cols, 2 ), cols - valwidth )
199
+ keywidth = max (cld (cols, 3 ), cols - valwidth )
177
200
end
178
201
else
179
202
rows = cols = typemax (Int)
@@ -182,20 +205,20 @@ function show(io::IO, ::MIME"text/plain", t::AbstractDict{K,V}) where {K,V}
182
205
for (i, (k, v)) in enumerate (t)
183
206
print (io, " \n " )
184
207
if i == rows < length (t)
185
- print (io, rpad (" ⋮" , keylen ), " => ⋮" )
208
+ print (io, rpad (" ⋮" , keywidth ), " => ⋮" )
186
209
break
187
210
end
188
211
189
212
if limit
190
- key = rpad ( _truncate_at_width_or_chars (get (recur_io, :color , false ), ks[i], keylen, " \r\n " ), keylen )
213
+ key = _truncate_at_width_or_chars (hascolor, ks[i], keywidth, true )
191
214
else
192
215
key = sprint (show, k, context= recur_io_k, sizehint= 0 )
193
216
end
194
217
print (recur_io, key)
195
218
print (io, " => " )
196
219
197
220
if limit
198
- val = _truncate_at_width_or_chars (get (recur_io, :color , false ), vs[i], cols - keylen, " \r\n " )
221
+ val = _truncate_at_width_or_chars (hascolor, vs[i], cols - keywidth )
199
222
print (io, val)
200
223
else
201
224
show (recur_io_v, v)
@@ -239,7 +262,7 @@ function show(io::IO, ::MIME"text/plain", t::AbstractSet{T}) where T
239
262
240
263
if limit
241
264
str = sprint (show, v, context= recur_io, sizehint= 0 )
242
- print (io, _truncate_at_width_or_chars (get (io, :color , false ), str, cols, " \r\n " ))
265
+ print (io, _truncate_at_width_or_chars (get (io, :color , false ), str, cols))
243
266
else
244
267
show (recur_io, v)
245
268
end
0 commit comments