Skip to content

Commit 596ab8a

Browse files
committed
Simplify fix
1 parent 5481dde commit 596ab8a

File tree

3 files changed

+66
-108
lines changed

3 files changed

+66
-108
lines changed

base/show.jl

Lines changed: 17 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -51,121 +51,41 @@ show(io::IO, ::MIME"text/plain", s::Splat) = show(io, s)
5151
const possible_ansi_regex = r"\x1B(?:[@-Z\\-_\[])"
5252
const start_ansi_regex = r"\G(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])"
5353

54-
const text_colors = Dict{Union{Symbol,Int},String}(
55-
:black => "\033[30m",
56-
:red => "\033[31m",
57-
:green => "\033[32m",
58-
:yellow => "\033[33m",
59-
:blue => "\033[34m",
60-
:magenta => "\033[35m",
61-
:cyan => "\033[36m",
62-
:white => "\033[37m",
63-
:light_black => "\033[90m", # gray
64-
:light_red => "\033[91m",
65-
:light_green => "\033[92m",
66-
:light_yellow => "\033[93m",
67-
:light_blue => "\033[94m",
68-
:light_magenta => "\033[95m",
69-
:light_cyan => "\033[96m",
70-
:light_white => "\033[97m",
71-
:normal => "\033[0m",
72-
:default => "\033[39m",
73-
:bold => "\033[1m",
74-
:underline => "\033[4m",
75-
:blink => "\033[5m",
76-
:reverse => "\033[7m",
77-
:hidden => "\033[8m",
78-
:nothing => "",
79-
)
80-
81-
for i in 0:255
82-
text_colors[i] = String(['\033', '[', '3', '8', ';', '5', ';', string(i)..., 'm'])
83-
end
84-
85-
const ansi_code_index = Dict{String,Int8}(x[2:end] => 6 for x in values(text_colors) if !isempty(x))
86-
merge!(ansi_code_index, Dict{String,Int8}(
87-
"[1m" => 1, # bold
88-
"[4m" => 2, # underline
89-
"[5m" => 3, # blink
90-
"[7m" => 4, # reverse
91-
"[8m" => 5, # hidden
92-
"[22m" => -1, # remove bold
93-
"[24m" => -2, # remove underline
94-
"[25m" => -3, # remove blink
95-
"[27m" => -4, # remove reverse
96-
"[28m" => -5, # remove hidden
97-
"[39m" => -6, # reset default color
98-
"[0m" => 0 # reset everything
99-
))
100-
101-
function find_lastidx_withcolor(str, width, chars, truncwidth, start)
102-
idx = lastidx = start
103-
truncidx = 0 # if str needs to be truncated, index of truncation.
104-
wid = textwidth(SubString(str, 1, idx)) # text width up to idx.
54+
function _find_lastidx(str, width, chars, truncwidth, start)
55+
idx = start
56+
lastidx = idx == 0 ? 0 : prevind(str, idx)
57+
wid = textwidth(SubString(str, 1, lastidx)) # text width up to lastidx.
10558
if wid width - truncwidth
106-
# ensure that truncidx is correctly set in the main loop
59+
# ensure that truncidx is correctly set in the main loop.
10760
idx = 1
10861
wid = lastidx = 0
10962
end
110-
ansi_mask = zero(UInt32) # one-hot encoding of the encountered ansi color characters.
111-
# For example, if `ansi_mask == (1 << 7) | (1 << 3) | (1 << 2)`, i.e. if
112-
# `ansi_mask == 0b10001100`, then the ansi delimiters number 3 ("blink") and number 2
113-
# ("underline") were encountered without their corresponding end delimiter, as well as
114-
# an unknown ansi character (represented by the bit in position 7).
63+
truncidx = 0 # if str needs to be truncated, index of truncation.
11564
stop = false # when set, only ansi delimiters will be kept as new characters.
116-
firstmatch = 0 # index of the first ansi delimiter
65+
firstmatch = 0 # index of the first ansi delimiter.
66+
noansi = true # set if the substring should be completed with a "\033[0m" delimiter.
11767
while true
11868
str_iter = iterate(str, idx)
11969
isnothing(str_iter) && break
12070
_lastidx = idx
12171
c, idx = str_iter
12272
m = c == '\033' ? match(start_ansi_regex, str, idx) : nothing
12373
stop && isnothing(m) && break
74+
last_lastidx = lastidx
12475
lastidx = _lastidx
12576
if !isnothing(m)
12677
firstmatch == 0 && (firstmatch = lastidx)
127-
ansi_idx = get(ansi_code_index, m.match, Int8(7))
128-
if ansi_idx > 0
129-
ansi_mask |= (one(UInt32) << (ansi_idx % UInt8)) # set the bit
130-
elseif ansi_idx < 0
131-
ansi_mask &= ~(one(UInt32) << (-ansi_idx % UInt8)) # erase the bit
132-
else # encountered code "\033[0m" a.k.a. erase all properties
133-
ansi_mask = zero(UInt32)
134-
end
78+
noansi = m.match == "[0m"
13579
s = sizeof(m.match)
13680
idx += s
137-
lastidx += s
81+
lastidx = idx - 1 # the last character of an ansi delimiter is always of size 1.
13882
continue
13983
end
14084
wid += textwidth(c)
141-
if wid >= (width - truncwidth) && truncidx == 0
142-
truncidx = lastidx
143-
end
85+
truncidx == 0 && wid > (width - truncwidth) && (truncidx = last_lastidx)
14486
stop = (wid >= width || c in chars)
14587
end
146-
if lastidx < lastindex(str) && truncidx < firstmatch
147-
ansi_mask = zero(UInt32) # disregard ansi delimiters if they are truncated away
148-
end
149-
return lastidx, truncidx, ansi_mask
150-
end
151-
152-
function find_lastidx_nocolor(str, width, chars, truncwidth)
153-
lastidx = 0
154-
truncidx = 0
155-
idx = 1
156-
wid = 0
157-
while true
158-
str_iter = iterate(str, idx)
159-
isnothing(str_iter) && break
160-
lastidx = idx
161-
c, idx = str_iter
162-
wid += textwidth(c)
163-
if wid >= (width - truncwidth) && truncidx == 0
164-
truncidx = lastidx
165-
end
166-
(wid >= width || c in chars) && break
167-
end
168-
return lastidx, truncidx
88+
return lastidx, truncidx, noansi || (lastidx < lastindex(str) && truncidx < firstmatch)
16989
end
17090

17191
function _truncate_at_width_or_chars(ignore_ansi::Bool, str, width, chars="", truncmark="")
@@ -174,19 +94,15 @@ function _truncate_at_width_or_chars(ignore_ansi::Bool, str, width, chars="", tr
17494
# possible_substring is a subset of str at least as large as the returned string
17595
possible_substring = SubString(str, 1, thisind(str, min(ncodeunits(str), width+1)))
17696
m = ignore_ansi ? match(possible_ansi_regex, possible_substring) : nothing
177-
if isnothing(m)
178-
lastidx, truncidx = find_lastidx_nocolor(str, width, chars, truncwidth)
179-
ansi_mask = zero(UInt32)
180-
else
181-
lastidx, truncidx, ansi_mask = find_lastidx_withcolor(str, width, chars, truncwidth, m.offset)
182-
end
97+
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)
18399
lastidx == 0 && return ""
184100
str[lastidx] in chars && (lastidx = prevind(str, lastidx))
185101
truncidx == 0 && (truncidx = lastidx)
186102
if lastidx < lastindex(str)
187-
return string(SubString(str, 1, truncidx), iszero(ansi_mask) ? "" : "\033[0m", truncmark)
103+
return string(SubString(str, 1, truncidx), noansi ? "" : "\033[0m", truncmark)
188104
else
189-
return iszero(ansi_mask) ? String(str) : string(str, "\033[0m")
105+
return noansi ? String(str) : string(str, "\033[0m")
190106
end
191107
end
192108

base/util.jl

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,37 @@
22

33
## printing with color ##
44

5+
const text_colors = Dict{Union{Symbol,Int},String}(
6+
:black => "\033[30m",
7+
:red => "\033[31m",
8+
:green => "\033[32m",
9+
:yellow => "\033[33m",
10+
:blue => "\033[34m",
11+
:magenta => "\033[35m",
12+
:cyan => "\033[36m",
13+
:white => "\033[37m",
14+
:light_black => "\033[90m", # gray
15+
:light_red => "\033[91m",
16+
:light_green => "\033[92m",
17+
:light_yellow => "\033[93m",
18+
:light_blue => "\033[94m",
19+
:light_magenta => "\033[95m",
20+
:light_cyan => "\033[96m",
21+
:light_white => "\033[97m",
22+
:normal => "\033[0m",
23+
:default => "\033[39m",
24+
:bold => "\033[1m",
25+
:underline => "\033[4m",
26+
:blink => "\033[5m",
27+
:reverse => "\033[7m",
28+
:hidden => "\033[8m",
29+
:nothing => "",
30+
)
31+
32+
for i in 0:255
33+
text_colors[i] = "\033[38;5;$(i)m"
34+
end
35+
536
const disable_text_style = Dict{Symbol,String}(
637
:bold => "\033[22m",
738
:underline => "\033[24m",

test/dict.jl

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -406,18 +406,19 @@ end
406406
d2 = Dict(:foo => RainbowString("bar"))
407407
str2 = sprint(io -> show(io, MIME("text/plain"), d2); context = (:displaysize=>(30,80), :color=>true, :limit=>true))
408408
@test !occursin('', str2)
409-
@test endswith(str2, "\033[39m")
409+
@test endswith(str2, "\033[0m")
410410

411411
d3 = Dict(:foo => RainbowString("bar", true))
412412
str3 = sprint(io -> show(io, MIME("text/plain"), d3); context = (:displaysize=>(30,80), :color=>true, :limit=>true))
413-
@test endswith(str3, "\033[22m\033[39m")
413+
@test !occursin('', str3)
414+
@test endswith(str3, "\033[0m")
414415

415416
d4 = Dict(RainbowString(randstring(8), true) => nothing)
416417
str4 = sprint(io -> show(io, MIME("text/plain"), d4); context = (:displaysize=>(30,20), :color=>true, :limit=>true))
417418
@test endswith(str4, "\033[0m… => nothing")
418419

419-
d5 = Dict(RainbowString(randstring(20), false, true, false) => nothing)
420-
str5 = sprint(io -> show(io, MIME("text/plain"), d5); context = (:displaysize=>(30,20), :color=>true, :limit=>true))
420+
d5 = Dict(RainbowString(randstring(30), false, true, false) => nothing)
421+
str5 = sprint(io -> show(io, MIME("text/plain"), d5); context = (:displaysize=>(30,30), :color=>true, :limit=>true))
421422
@test endswith(str5, "\033[0m… => nothing")
422423

423424
d6 = Dict(randstring(8) => RainbowString(randstring(30), true, true, false) for _ in 1:3)
@@ -426,21 +427,31 @@ end
426427
@test all(endswith("\033[0m…"), lines6[2:end])
427428
@test all(x -> length(x) > 100, lines6[2:end])
428429

429-
d7 = Dict(randstring(8) => RainbowString(randstring(30), false, false))
430+
d7 = Dict(randstring(8) => RainbowString(randstring(30)))
430431
str7 = sprint(io -> show(io, MIME("text/plain"), d7); context = (:displaysize=>(30,20), :color=>true, :limit=>true))
431432
line7 = split(str7, '\n')[2]
432433
@test endswith(line7, "\033[0m…")
433434
@test length(line7) > 100
434435

435436
d8 = Dict(:x => RainbowString(randstring(10), false, false, false, 6))
436-
str8 = sprint(io -> show(io, MIME("text/plain"), d8); context = (:displaysize=>(30,15), :color=>true, :limit=>true))
437+
str8 = sprint(io -> show(io, MIME("text/plain"), d8); context = (:displaysize=>(30,14), :color=>true, :limit=>true))
437438
line8 = split(str8, '\n')[2]
438439
@test !occursin(line8, "\033[")
439-
@test length(line8) == 15
440+
@test length(line8) == 14
440441
str8_long = sprint(io -> show(io, MIME("text/plain"), d8); context = (:displaysize=>(30,16), :color=>true, :limit=>true))
441442
line8_long = split(str8_long, '\n')[2]
442443
@test endswith(line8_long, "\033[0m…")
443444
@test length(line8_long) > 20
445+
446+
d9 = Dict(:x => RainbowString(repeat('', 5), false, true, false))
447+
str9 = sprint(io -> show(io, MIME("text/plain"), d9); context = (:displaysize=>(30,15), :color=>true, :limit=>true))
448+
@test endswith(str9, "\033[0m…")
449+
@test count('', str9) == 3
450+
451+
d10 = Dict(:xy => RainbowString(repeat('', 5), false, true, false))
452+
str10 = sprint(io -> show(io, MIME("text/plain"), d10); context = (:displaysize=>(30,15), :color=>true, :limit=>true))
453+
@test endswith(str10, "\033[0m…")
454+
@test count('', str10) == 2
444455
end
445456

446457
@testset "Issue #15739" begin # Compact REPL printouts of an `AbstractDict` use brackets when appropriate

0 commit comments

Comments
 (0)