Skip to content

Commit

Permalink
Simplify fix
Browse files Browse the repository at this point in the history
  • Loading branch information
Liozou committed Jun 6, 2022
1 parent 5481dde commit 596ab8a
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 108 deletions.
118 changes: 17 additions & 101 deletions base/show.jl
Original file line number Diff line number Diff line change
Expand Up @@ -51,121 +51,41 @@ show(io::IO, ::MIME"text/plain", s::Splat) = show(io, s)
const possible_ansi_regex = r"\x1B(?:[@-Z\\-_\[])"
const start_ansi_regex = r"\G(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])"

const text_colors = Dict{Union{Symbol,Int},String}(
:black => "\033[30m",
:red => "\033[31m",
:green => "\033[32m",
:yellow => "\033[33m",
:blue => "\033[34m",
:magenta => "\033[35m",
:cyan => "\033[36m",
:white => "\033[37m",
:light_black => "\033[90m", # gray
:light_red => "\033[91m",
:light_green => "\033[92m",
:light_yellow => "\033[93m",
:light_blue => "\033[94m",
:light_magenta => "\033[95m",
:light_cyan => "\033[96m",
:light_white => "\033[97m",
:normal => "\033[0m",
:default => "\033[39m",
:bold => "\033[1m",
:underline => "\033[4m",
:blink => "\033[5m",
:reverse => "\033[7m",
:hidden => "\033[8m",
:nothing => "",
)

for i in 0:255
text_colors[i] = String(['\033', '[', '3', '8', ';', '5', ';', string(i)..., 'm'])
end

const ansi_code_index = Dict{String,Int8}(x[2:end] => 6 for x in values(text_colors) if !isempty(x))
merge!(ansi_code_index, Dict{String,Int8}(
"[1m" => 1, # bold
"[4m" => 2, # underline
"[5m" => 3, # blink
"[7m" => 4, # reverse
"[8m" => 5, # hidden
"[22m" => -1, # remove bold
"[24m" => -2, # remove underline
"[25m" => -3, # remove blink
"[27m" => -4, # remove reverse
"[28m" => -5, # remove hidden
"[39m" => -6, # reset default color
"[0m" => 0 # reset everything
))

function find_lastidx_withcolor(str, width, chars, truncwidth, start)
idx = lastidx = start
truncidx = 0 # if str needs to be truncated, index of truncation.
wid = textwidth(SubString(str, 1, idx)) # text width up to idx.
function _find_lastidx(str, width, chars, truncwidth, start)
idx = start
lastidx = idx == 0 ? 0 : prevind(str, idx)
wid = textwidth(SubString(str, 1, lastidx)) # text width up to lastidx.
if wid width - truncwidth
# ensure that truncidx is correctly set in the main loop
# ensure that truncidx is correctly set in the main loop.
idx = 1
wid = lastidx = 0
end
ansi_mask = zero(UInt32) # one-hot encoding of the encountered ansi color characters.
# For example, if `ansi_mask == (1 << 7) | (1 << 3) | (1 << 2)`, i.e. if
# `ansi_mask == 0b10001100`, then the ansi delimiters number 3 ("blink") and number 2
# ("underline") were encountered without their corresponding end delimiter, as well as
# an unknown ansi character (represented by the bit in position 7).
truncidx = 0 # if str needs to be truncated, index of truncation.
stop = false # when set, only ansi delimiters will be kept as new characters.
firstmatch = 0 # index of the first ansi delimiter
firstmatch = 0 # index of the first ansi delimiter.
noansi = true # set if the substring should be completed with a "\033[0m" delimiter.
while true
str_iter = iterate(str, idx)
isnothing(str_iter) && break
_lastidx = idx
c, idx = str_iter
m = c == '\033' ? match(start_ansi_regex, str, idx) : nothing
stop && isnothing(m) && break
last_lastidx = lastidx
lastidx = _lastidx
if !isnothing(m)
firstmatch == 0 && (firstmatch = lastidx)
ansi_idx = get(ansi_code_index, m.match, Int8(7))
if ansi_idx > 0
ansi_mask |= (one(UInt32) << (ansi_idx % UInt8)) # set the bit
elseif ansi_idx < 0
ansi_mask &= ~(one(UInt32) << (-ansi_idx % UInt8)) # erase the bit
else # encountered code "\033[0m" a.k.a. erase all properties
ansi_mask = zero(UInt32)
end
noansi = m.match == "[0m"
s = sizeof(m.match)
idx += s
lastidx += s
lastidx = idx - 1 # the last character of an ansi delimiter is always of size 1.
continue
end
wid += textwidth(c)
if wid >= (width - truncwidth) && truncidx == 0
truncidx = lastidx
end
truncidx == 0 && wid > (width - truncwidth) && (truncidx = last_lastidx)
stop = (wid >= width || c in chars)
end
if lastidx < lastindex(str) && truncidx < firstmatch
ansi_mask = zero(UInt32) # disregard ansi delimiters if they are truncated away
end
return lastidx, truncidx, ansi_mask
end

function find_lastidx_nocolor(str, width, chars, truncwidth)
lastidx = 0
truncidx = 0
idx = 1
wid = 0
while true
str_iter = iterate(str, idx)
isnothing(str_iter) && break
lastidx = idx
c, idx = str_iter
wid += textwidth(c)
if wid >= (width - truncwidth) && truncidx == 0
truncidx = lastidx
end
(wid >= width || c in chars) && break
end
return lastidx, truncidx
return lastidx, truncidx, noansi || (lastidx < lastindex(str) && truncidx < firstmatch)
end

function _truncate_at_width_or_chars(ignore_ansi::Bool, str, width, chars="", truncmark="")
Expand All @@ -174,19 +94,15 @@ function _truncate_at_width_or_chars(ignore_ansi::Bool, str, width, chars="", tr
# possible_substring is a subset of str at least as large as the returned string
possible_substring = SubString(str, 1, thisind(str, min(ncodeunits(str), width+1)))
m = ignore_ansi ? match(possible_ansi_regex, possible_substring) : nothing
if isnothing(m)
lastidx, truncidx = find_lastidx_nocolor(str, width, chars, truncwidth)
ansi_mask = zero(UInt32)
else
lastidx, truncidx, ansi_mask = find_lastidx_withcolor(str, width, chars, truncwidth, m.offset)
end
start_offset = isnothing(m) ? thisind(str, min(ncodeunits(str), width-truncwidth)) : m.offset
lastidx, truncidx, noansi = _find_lastidx(str, width, chars, truncwidth, start_offset)
lastidx == 0 && return ""
str[lastidx] in chars && (lastidx = prevind(str, lastidx))
truncidx == 0 && (truncidx = lastidx)
if lastidx < lastindex(str)
return string(SubString(str, 1, truncidx), iszero(ansi_mask) ? "" : "\033[0m", truncmark)
return string(SubString(str, 1, truncidx), noansi ? "" : "\033[0m", truncmark)
else
return iszero(ansi_mask) ? String(str) : string(str, "\033[0m")
return noansi ? String(str) : string(str, "\033[0m")
end
end

Expand Down
31 changes: 31 additions & 0 deletions base/util.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,37 @@

## printing with color ##

const text_colors = Dict{Union{Symbol,Int},String}(
:black => "\033[30m",
:red => "\033[31m",
:green => "\033[32m",
:yellow => "\033[33m",
:blue => "\033[34m",
:magenta => "\033[35m",
:cyan => "\033[36m",
:white => "\033[37m",
:light_black => "\033[90m", # gray
:light_red => "\033[91m",
:light_green => "\033[92m",
:light_yellow => "\033[93m",
:light_blue => "\033[94m",
:light_magenta => "\033[95m",
:light_cyan => "\033[96m",
:light_white => "\033[97m",
:normal => "\033[0m",
:default => "\033[39m",
:bold => "\033[1m",
:underline => "\033[4m",
:blink => "\033[5m",
:reverse => "\033[7m",
:hidden => "\033[8m",
:nothing => "",
)

for i in 0:255
text_colors[i] = "\033[38;5;$(i)m"
end

const disable_text_style = Dict{Symbol,String}(
:bold => "\033[22m",
:underline => "\033[24m",
Expand Down
25 changes: 18 additions & 7 deletions test/dict.jl
Original file line number Diff line number Diff line change
Expand Up @@ -406,18 +406,19 @@ end
d2 = Dict(:foo => RainbowString("bar"))
str2 = sprint(io -> show(io, MIME("text/plain"), d2); context = (:displaysize=>(30,80), :color=>true, :limit=>true))
@test !occursin('', str2)
@test endswith(str2, "\033[39m")
@test endswith(str2, "\033[0m")

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

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

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

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

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

d8 = Dict(:x => RainbowString(randstring(10), false, false, false, 6))
str8 = sprint(io -> show(io, MIME("text/plain"), d8); context = (:displaysize=>(30,15), :color=>true, :limit=>true))
str8 = sprint(io -> show(io, MIME("text/plain"), d8); context = (:displaysize=>(30,14), :color=>true, :limit=>true))
line8 = split(str8, '\n')[2]
@test !occursin(line8, "\033[")
@test length(line8) == 15
@test length(line8) == 14
str8_long = sprint(io -> show(io, MIME("text/plain"), d8); context = (:displaysize=>(30,16), :color=>true, :limit=>true))
line8_long = split(str8_long, '\n')[2]
@test endswith(line8_long, "\033[0m…")
@test length(line8_long) > 20

d9 = Dict(:x => RainbowString(repeat('', 5), false, true, false))
str9 = sprint(io -> show(io, MIME("text/plain"), d9); context = (:displaysize=>(30,15), :color=>true, :limit=>true))
@test endswith(str9, "\033[0m…")
@test count('', str9) == 3

d10 = Dict(:xy => RainbowString(repeat('', 5), false, true, false))
str10 = sprint(io -> show(io, MIME("text/plain"), d10); context = (:displaysize=>(30,15), :color=>true, :limit=>true))
@test endswith(str10, "\033[0m…")
@test count('', str10) == 2
end

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

0 comments on commit 596ab8a

Please sign in to comment.