@@ -48,58 +48,40 @@ show(io::IO, ::MIME"text/plain", c::ComposedFunction) = show(io, c)
48
48
show (io:: IO , :: MIME"text/plain" , c:: Returns ) = show (io, c)
49
49
show (io:: IO , :: MIME"text/plain" , s:: Splat ) = show (io, s)
50
50
51
- const ansi_regex = r" \G\ x 1B(?:[@-Z\\ -_]|\[ [0-?]*[ -/]*[@-~])"
51
+ const ansi_regex = r" (?s)(?: \ x 1B(?:[@-Z\\ -_]|\[ [0-?]*[ -/]*[@-~]))|. "
52
52
53
53
# Pseudo-character representing an ANSI delimiter
54
54
struct ANSIDelimiter
55
55
del:: SubString{String}
56
56
end
57
- Base . ncodeunits (c:: ANSIDelimiter ) = ncodeunits (c. del)
58
- Base . textwidth (:: ANSIDelimiter ) = 0
57
+ ncodeunits (c:: ANSIDelimiter ) = ncodeunits (c. del)
58
+ textwidth (:: ANSIDelimiter ) = 0
59
59
60
- # String wrapper whose indexing yields either a Char or an ANSIDelimiter
61
- struct ANSIString{T <: AbstractString }
62
- str :: T
60
+ # An iterator similar to `pairs(::String)` but whose values are Char or ANSIDelimiter
61
+ struct ANSIIterator
62
+ captures :: RegexMatchIterator
63
63
end
64
+ ANSIIterator (s:: AbstractString ) = ANSIIterator (eachmatch (ansi_regex, s))
64
65
65
- Base. IteratorSize (:: Type{ANSIString{T}} ) where {T} = Base. SizeUnknown ()
66
- Base. eltype (:: Type{ANSIString{T}} ) where {T} = Union{eltype (T),ANSIDelimiter}
67
- function Base. getindex (s:: ANSIString , i)
68
- m = match (ansi_regex, s. str, i)
69
- m isa RegexMatch && return ANSIDelimiter (m. match)
70
- return s. str[i]
66
+ IteratorSize (:: Type{ANSIIterator} ) = SizeUnknown ()
67
+ eltype (:: Type{ANSIIterator} ) = Pair{Int, Union{Char,ANSIDelimiter}}
68
+ function iterate (I:: ANSIIterator , (i, m_st)= (1 , iterate (I. captures)))
69
+ m_st === nothing && return nothing
70
+ m, (j, new_m_st) = m_st
71
+ c = lastindex (m. match) == 1 ? only (m. match) : ANSIDelimiter (m. match)
72
+ return (i => c, (j, iterate (I. captures, (j, new_m_st))))
71
73
end
74
+ textwidth (I:: ANSIIterator ) = mapreduce (textwidth∘ last, + , I; init= 0 )
72
75
73
- function Base. iterate (s:: ANSIString , i= firstindex (s. str))
74
- y = iterate (pairs (s), i) # pairs(::ANSIString) is defined as ANSIStringPairs
75
- y === nothing && return nothing
76
- ((_, c), next) = y
77
- return (c, next)
78
- end
79
- Base. textwidth (s:: ANSIString ) = mapreduce (textwidth, + , s; init= 0 )
80
-
81
- # An iterator similar to `pairs(::String)` but whose values are either Char or ANSIDelimiter
82
- struct ANSIStringPairs{T<: AbstractString }
83
- s:: ANSIString{T}
84
- end
85
-
86
- Base. IteratorSize (:: Type{ANSIStringPairs{T}} ) where {T} = Base. SizeUnknown ()
87
- Base. eltype (:: Type{ANSIStringPairs{T}} ) where {T} = Pair{Int, Union{eltype (T),ANSIDelimiter}}
88
- function Base. iterate (e:: ANSIStringPairs , i= firstindex (e. s. str))
89
- i > ncodeunits (e. s. str) && return nothing
90
- c = e. s[i]
91
- return (i => c, i + ncodeunits (c))
92
- end
93
- Base. pairs (s:: ANSIString ) = ANSIStringPairs (s)
94
-
95
-
96
- function _find_lastidx (str, width, chars, truncwidth, ignore_ANSI)
97
- lastidx = 0
98
- truncidx = 0 # if str needs to be truncated, index of truncation.
99
- stop = false # when set, only ANSI delimiters will be kept as new characters.
76
+ function _truncate_at_width_or_chars (ignore_ANSI:: Bool , str, width, rpad= false , chars= " \r\n " , truncmark= " …" )
77
+ truncwidth = textwidth (truncmark)
78
+ (width <= 0 || width < truncwidth) && return " "
79
+ wid = truncidx = lastidx = 0
80
+ # if str needs to be truncated, truncidx is the index of truncation.
81
+ stop = false # once set, only ANSI delimiters will be kept as new characters.
100
82
needANSIend = false # set if the last ANSI delimiter before truncidx is not "\033[0m".
101
- wid = 0
102
- for (i, c) in (ignore_ANSI ? pairs ( ANSIString (str)) : pairs (str))
83
+ I = ignore_ANSI ? ANSIIterator (str) : pairs (str)
84
+ for (i, c) in I
103
85
if c isa ANSIDelimiter
104
86
truncidx == 0 && (needANSIend = c != " \0 33[0m" )
105
87
lastidx = i + ncodeunits (c) - 1
@@ -112,20 +94,11 @@ function _find_lastidx(str, width, chars, truncwidth, ignore_ANSI)
112
94
stop = wid >= width
113
95
end
114
96
end
115
- truncidx == 0 && (truncidx = lastidx)
116
- return lastidx, truncidx, needANSIend, wid
117
- end
118
-
119
- function _truncate_at_width_or_chars (ignore_ANSI:: Bool , str, width, rpad= false , chars= " \r\n " , truncmark= " …" )
120
- truncwidth = textwidth (truncmark)
121
- (width <= 0 || width < truncwidth) && return rpad ? ' ' ^ width : " "
122
- # possible_substring is a subset of str at least as large as the returned string
123
- possible_substring = SubString (str, 1 , thisind (str, min (ncodeunits (str), width+ 1 )))
124
- lastidx, truncidx, needANSIend, wid = _find_lastidx (str, width, chars, truncwidth, ignore_ANSI)
125
97
lastidx == 0 && return rpad ? ' ' ^ width : " "
126
98
str[lastidx] in chars && (lastidx = prevind (str, lastidx))
127
99
ANSIend = needANSIend ? " \0 33[0m" : " "
128
100
pad = rpad ? repeat (' ' , max (0 , width- wid)) : " "
101
+ truncidx == 0 && (truncidx = lastidx)
129
102
if lastidx < lastindex (str)
130
103
return string (SubString (str, 1 , truncidx), ANSIend, truncmark, pad)
131
104
else
@@ -197,8 +170,8 @@ function show(io::IO, ::MIME"text/plain", t::AbstractDict{K,V}) where {K,V}
197
170
i > rows && break
198
171
ks[i] = sprint (show, k, context= recur_io_k, sizehint= 0 )
199
172
vs[i] = sprint (show, v, context= recur_io_v, sizehint= 0 )
200
- keywidth = clamp (hascolor ? textwidth (ANSIString (ks[i])) : textwidth (ks[i]), keywidth, cols)
201
- valwidth = clamp (hascolor ? textwidth (ANSIString (vs[i])) : textwidth (vs[i]), valwidth, cols)
173
+ keywidth = clamp (hascolor ? textwidth (ANSIIterator (ks[i])) : textwidth (ks[i]), keywidth, cols)
174
+ valwidth = clamp (hascolor ? textwidth (ANSIIterator (vs[i])) : textwidth (vs[i]), valwidth, cols)
202
175
end
203
176
if keywidth > max (div (cols, 2 ), cols - valwidth)
204
177
keywidth = max (cld (cols, 3 ), cols - valwidth)
0 commit comments