Skip to content

Commit 592e4a4

Browse files
committed
Take into account color and unicode in matrix alignment
Without this, alignment would count characters rather than textwidth as well as counting inline escape sequences in colored output. Fix that by using uncolored printing for alignment and textwidth rather than number of codepoints.
1 parent 6dae654 commit 592e4a4

File tree

2 files changed

+35
-16
lines changed

2 files changed

+35
-16
lines changed

base/show.jl

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2783,6 +2783,9 @@ function dump(arg; maxdepth=DUMP_DEFAULT_MAXDEPTH)
27832783
dump(IOContext(stdout::IO, :limit => true, :module => mod), arg; maxdepth=maxdepth)
27842784
end
27852785

2786+
nocolor(io::IO) = IOContext(io, :color => false)
2787+
alignment_from_show(io::IO, x::Any) =
2788+
textwidth(sprint(show, x, context=nocolor(io), sizehint=0))
27862789

27872790
"""
27882791
`alignment(io, X)` returns a tuple (left,right) showing how many characters are
@@ -2800,35 +2803,38 @@ julia> Base.alignment(stdout, 1 + 10im)
28002803
(3, 5)
28012804
```
28022805
"""
2803-
alignment(io::IO, x::Any) = (0, length(sprint(show, x, context=io, sizehint=0)))
2804-
alignment(io::IO, x::Number) = (length(sprint(show, x, context=io, sizehint=0)), 0)
2805-
alignment(io::IO, x::Integer) = (length(sprint(show, x, context=io, sizehint=0)), 0)
2806+
alignment(io::IO, x::Any) = (0, alignment_from_show(io, x))
2807+
alignment(io::IO, x::Number) = (alignment_from_show(io, x), 0)
2808+
alignment(io::IO, x::Integer) = (alignment_from_show(io, x), 0)
28062809
function alignment(io::IO, x::Real)
2807-
m = match(r"^(.*?)((?:[\.eEfF].*)?)$", sprint(show, x, context=io, sizehint=0))
2808-
m === nothing ? (length(sprint(show, x, context=io, sizehint=0)), 0) :
2809-
(length(m.captures[1]), length(m.captures[2]))
2810+
s = sprint(show, x, context=nocolor(io), sizehint=0)
2811+
m = match(r"^(.*?)((?:[\.eEfF].*)?)$", s)
2812+
m === nothing ? (textwidth(s), 0) :
2813+
(textwidth(m.captures[1]), textwidth(m.captures[2]))
28102814
end
28112815
function alignment(io::IO, x::Complex)
2812-
m = match(r"^(.*[^ef][\+\-])(.*)$", sprint(show, x, context=io, sizehint=0))
2813-
m === nothing ? (length(sprint(show, x, context=io, sizehint=0)), 0) :
2814-
(length(m.captures[1]), length(m.captures[2]))
2816+
s = sprint(show, x, context=nocolor(io), sizehint=0)
2817+
m = match(r"^(.*[^ef][\+\-])(.*)$", s)
2818+
m === nothing ? (textwidth(s), 0) :
2819+
(textwidth(m.captures[1]), textwidth(m.captures[2]))
28152820
end
28162821
function alignment(io::IO, x::Rational)
2817-
m = match(r"^(.*?/)(/.*)$", sprint(show, x, context=io, sizehint=0))
2818-
m === nothing ? (length(sprint(show, x, context=io, sizehint=0)), 0) :
2819-
(length(m.captures[1]), length(m.captures[2]))
2822+
s = sprint(show, x, context=nocolor(io), sizehint=0)
2823+
m = match(r"^(.*?/)(/.*)$", s)
2824+
m === nothing ? (textwidth(s), 0) :
2825+
(textwidth(m.captures[1]), textwidth(m.captures[2]))
28202826
end
28212827

28222828
function alignment(io::IO, x::Pair)
2823-
s = sprint(show, x, context=io, sizehint=0)
2829+
fullwidth = alignment_from_show(io, x)
28242830
if !isdelimited(io, x) # i.e. use "=>" for display
28252831
ctx = IOContext(io, :typeinfo => gettypeinfos(io, x)[1])
2826-
left = length(sprint(show, x.first, context=ctx, sizehint=0))
2832+
left = alignment_from_show(ctx, x.first)
28272833
left += 2 * !isdelimited(ctx, x.first) # for parens around p.first
28282834
left += !(get(io, :compact, false)::Bool) # spaces are added around "=>"
2829-
(left+1, length(s)-left-1) # +1 for the "=" part of "=>"
2835+
(left+1, fullwidth-left-1) # +1 for the "=" part of "=>"
28302836
else
2831-
(0, length(s)) # as for x::Any
2837+
(0, fullwidth) # as for x::Any
28322838
end
28332839
end
28342840

test/show.jl

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2360,3 +2360,16 @@ end
23602360
@test sprint(show, setenv(setcpuaffinity(`true`, [1, 2]), "A" => "B")) ==
23612361
"""setenv(setcpuaffinity(`true`, [1, 2]),["A=B"])"""
23622362
end
2363+
2364+
# Test that alignment takes into account unicode and computes alignment without
2365+
# color/formatting.
2366+
2367+
struct ColoredLetter; end
2368+
Base.show(io::IO, ces::ColoredLetter) = Base.printstyled(io, 'A'; color=:red)
2369+
2370+
struct ⛵; end
2371+
Base.show(io::IO, ces::⛵) = Base.print(io, '')
2372+
2373+
@test Base.alignment(stdout, ()) == (0, 2)
2374+
@test Base.alignment(IOContext(IOBuffer(), :color=>true), ColoredLetter()) == (0, 1)
2375+
@test Base.alignment(IOContext(IOBuffer(), :color=>false), ColoredLetter()) == (0, 1)

0 commit comments

Comments
 (0)