@@ -50,6 +50,7 @@ show(io::IO, ::MIME"text/plain", s::Splat) = show(io, s)
5050
5151const possible_ansi_regex = r" \x 1B(?:[@-Z\\ -_\[ ])"
5252const start_ansi_regex = r" \G (?:[@-Z\\ -_]|\[ [0-?]*[ -/]*[@-~])"
53+ const next_ansi_regexes = r" .(?:\x 1B(?:[@-Z\\ -_]|\[ [0-?]*[ -/]*[@-~]))+"
5354
5455function _find_lastidx (str, width, chars, truncwidth, start)
5556 idx = start
@@ -76,7 +77,7 @@ function _find_lastidx(str, width, chars, truncwidth, start)
7677 if ! isnothing (m)
7778 firstmatch == 0 && (firstmatch = lastidx)
7879 noansi = m. match == " [0m"
79- s = sizeof (m. match)
80+ s = ncodeunits (m. match)
8081 idx += s
8182 lastidx = idx - 1 # the last character of an ansi delimiter is always of size 1.
8283 continue
@@ -85,27 +86,48 @@ function _find_lastidx(str, width, chars, truncwidth, start)
8586 truncidx == 0 && wid > (width - truncwidth) && (truncidx = last_lastidx)
8687 stop = (wid >= width || c in chars)
8788 end
88- return lastidx, truncidx, noansi || (lastidx < lastindex (str) && truncidx < firstmatch)
89+ return lastidx, truncidx, noansi || (lastidx < lastindex (str) && truncidx < firstmatch), wid
8990end
9091
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= " …" )
9293 truncwidth = textwidth (truncmark)
93- (width <= 0 || width < truncwidth) && return " "
94+ (width <= 0 || width < truncwidth) && return rpad ? ' ' ^ width : " "
9495 # possible_substring is a subset of str at least as large as the returned string
9596 possible_substring = SubString (str, 1 , thisind (str, min (ncodeunits (str), width+ 1 )))
9697 m = ignore_ansi ? match (possible_ansi_regex, possible_substring) : nothing
9798 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 : " "
100101 str[lastidx] in chars && (lastidx = prevind (str, lastidx))
101102 truncidx == 0 && (truncidx = lastidx)
103+ endansi = noansi ? " " : " \0 33[0m"
104+ pad = rpad ? repeat (' ' , max (0 , width- wid)) : " "
102105 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 )
104107 else
105- return noansi ? String (str) : string (str, " \0 33[0m " )
108+ return string (str, endansi, pad )
106109 end
107110end
108111
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+
109131function show (io:: IO , :: MIME"text/plain" , iter:: Union{KeySet,ValueIterator} )
110132 isempty (iter) && get (io, :compact , false ) && return show (io, iter)
111133 summary (io, iter)
@@ -129,7 +151,7 @@ function show(io::IO, ::MIME"text/plain", iter::Union{KeySet,ValueIterator})
129151
130152 if limit
131153 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)
133155 print (io, str)
134156 else
135157 show (io, v)
@@ -161,19 +183,20 @@ function show(io::IO, ::MIME"text/plain", t::AbstractDict{K,V}) where {K,V}
161183 rows -= 1 # Subtract the summary
162184
163185 # determine max key width to align the output, caching the strings
186+ hascolor = get (recur_io, :color , false )
164187 ks = Vector {String} (undef, min (rows, length (t)))
165188 vs = Vector {String} (undef, min (rows, length (t)))
166- keylen = 0
167- vallen = 0
189+ keywidth = 0
190+ valwidth = 0
168191 for (i, (k, v)) in enumerate (t)
169192 i > rows && break
170193 ks[i] = sprint (show, k, context= recur_io_k, sizehint= 0 )
171194 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)
174197 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 )
177200 end
178201 else
179202 rows = cols = typemax (Int)
@@ -182,20 +205,20 @@ function show(io::IO, ::MIME"text/plain", t::AbstractDict{K,V}) where {K,V}
182205 for (i, (k, v)) in enumerate (t)
183206 print (io, " \n " )
184207 if i == rows < length (t)
185- print (io, rpad (" ⋮" , keylen ), " => ⋮" )
208+ print (io, rpad (" ⋮" , keywidth ), " => ⋮" )
186209 break
187210 end
188211
189212 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 )
191214 else
192215 key = sprint (show, k, context= recur_io_k, sizehint= 0 )
193216 end
194217 print (recur_io, key)
195218 print (io, " => " )
196219
197220 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 )
199222 print (io, val)
200223 else
201224 show (recur_io_v, v)
@@ -239,7 +262,7 @@ function show(io::IO, ::MIME"text/plain", t::AbstractSet{T}) where T
239262
240263 if limit
241264 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))
243266 else
244267 show (recur_io, v)
245268 end
0 commit comments