From 9b72a2c76cf1ab7b3cc99bb613012b819309f86d Mon Sep 17 00:00:00 2001 From: Rafael Fourquet Date: Mon, 20 Nov 2017 19:25:08 +0100 Subject: [PATCH 1/2] move array printing functions to arrayshow.jl --- base/arrayshow.jl | 395 ++++++++++++++++++++++++++++++++++++++++++++++ base/show.jl | 392 --------------------------------------------- base/sysimg.jl | 1 + 3 files changed, 396 insertions(+), 392 deletions(-) create mode 100644 base/arrayshow.jl diff --git a/base/arrayshow.jl b/base/arrayshow.jl new file mode 100644 index 0000000000000..38ae8c280274e --- /dev/null +++ b/base/arrayshow.jl @@ -0,0 +1,395 @@ +""" +Unexported convenience function used in body of `replace_in_print_matrix` +methods. By default returns a string of the same width as original with a +centered cdot, used in printing of structural zeros of structured matrices. +Accept keyword args `c` for alternate single character marker. +""" +function replace_with_centered_mark(s::AbstractString;c::Char = '⋅') + N = length(s) + return join(setindex!([" " for i=1:N],string(c),ceil(Int,N/2))) +end + +const undef_ref_alignment = (3,3) + +""" +`alignment(X, rows, cols, cols_if_complete, cols_otherwise, sep)` returns the +alignment for specified parts of array `X`, returning the (left,right) info. +It will look in X's `rows`, `cols` (both lists of indices) +and figure out what's needed to be fully aligned, for example looking all +the way down a column and finding out the maximum size of each element. +Parameter `sep::Integer` is number of spaces to put between elements. +`cols_if_complete` and `cols_otherwise` indicate screen width to use. +Alignment is reported as a vector of (left,right) tuples, one for each +column going across the screen. +""" +function alignment(io::IO, X::AbstractVecOrMat, + rows::AbstractVector, cols::AbstractVector, + cols_if_complete::Integer, cols_otherwise::Integer, sep::Integer) + a = Tuple{Int, Int}[] + for j in cols # need to go down each column one at a time + l = r = 0 + for i in rows # plumb down and see what largest element sizes are + if isassigned(X,i,j) + aij = alignment(io, X[i,j]) + else + aij = undef_ref_alignment + end + l = max(l, aij[1]) # left characters + r = max(r, aij[2]) # right characters + end + push!(a, (l, r)) # one tuple per column of X, pruned to screen width + if length(a) > 1 && sum(map(sum,a)) + sep*length(a) >= cols_if_complete + pop!(a) # remove this latest tuple if we're already beyond screen width + break + end + end + if 1 < length(a) < length(indices(X,2)) + while sum(map(sum,a)) + sep*length(a) >= cols_otherwise + pop!(a) + end + end + return a +end + +""" +`print_matrix_row(io, X, A, i, cols, sep)` produces the aligned output for +a single matrix row X[i, cols] where the desired list of columns is given. +The corresponding alignment A is used, and the separation between elements +is specified as string sep. +`print_matrix_row` will also respect compact output for elements. +""" +function print_matrix_row(io::IO, + X::AbstractVecOrMat, A::Vector, + i::Integer, cols::AbstractVector, sep::AbstractString) + isempty(A) || first(indices(cols,1)) == 1 || throw(DimensionMismatch("indices of cols ($(indices(cols,1))) must start at 1")) + for k = 1:length(A) + j = cols[k] + if isassigned(X,Int(i),Int(j)) # isassigned accepts only `Int` indices + x = X[i,j] + a = alignment(io, x) + sx = sprint(0, show, x, env=io) + else + a = undef_ref_alignment + sx = undef_ref_str + end + l = repeat(" ", A[k][1]-a[1]) # pad on left and right as needed + r = repeat(" ", A[k][2]-a[2]) + prettysx = replace_in_print_matrix(X,i,j,sx) + print(io, l, prettysx, r) + if k < length(A); print(io, sep); end + end +end + + +""" +`print_matrix_vdots` is used to show a series of vertical ellipsis instead +of a bunch of rows for long matrices. Not only is the string vdots shown +but it also repeated every M elements if desired. +""" +function print_matrix_vdots(io::IO, vdots::AbstractString, + A::Vector, sep::AbstractString, M::Integer, m::Integer) + for k = 1:length(A) + w = A[k][1] + A[k][2] + if k % M == m + l = repeat(" ", max(0, A[k][1]-length(vdots))) + r = repeat(" ", max(0, w-length(vdots)-length(l))) + print(io, l, vdots, r) + else + print(io, repeat(" ", w)) + end + if k < length(A); print(io, sep); end + end +end + +""" + print_matrix(io::IO, mat, pre, sep, post, hdots, vdots, ddots, hmod, vmod) + +Prints a matrix with limited output size. If `io` sets `:limit` to true, +then only the corners of the matrix are printed, separated with vertical, +horizontal, and diagonal ellipses as appropriate. +Optional arguments are string pre (printed before the matrix, e.g. an opening bracket) +which will cause a corresponding same-size indent on following rows, and +string post (printed at the end of the last row of the matrix). +Also options to use different ellipsis characters hdots, vdots, ddots. +These are repeated every hmod or vmod elements. +""" +function print_matrix(io::IO, X::AbstractVecOrMat, + pre::AbstractString = " ", # pre-matrix string + sep::AbstractString = " ", # separator between elements + post::AbstractString = "", # post-matrix string + hdots::AbstractString = " \u2026 ", + vdots::AbstractString = "\u22ee", + ddots::AbstractString = " \u22f1 ", + hmod::Integer = 5, vmod::Integer = 5) + if !get(io, :limit, false) + screenheight = screenwidth = typemax(Int) + else + sz = displaysize(io) + screenheight, screenwidth = sz[1] - 4, sz[2] + end + screenwidth -= length(pre) + length(post) + presp = repeat(" ", length(pre)) # indent each row to match pre string + postsp = "" + @assert textwidth(hdots) == textwidth(ddots) + sepsize = length(sep) + rowsA, colsA = indices(X,1), indices(X,2) + m, n = length(rowsA), length(colsA) + # To figure out alignments, only need to look at as many rows as could + # fit down screen. If screen has at least as many rows as A, look at A. + # If not, then we only need to look at the first and last chunks of A, + # each half a screen height in size. + halfheight = div(screenheight,2) + if m > screenheight + rowsA = [rowsA[1:halfheight]; rowsA[m-div(screenheight-1,2)+1:m]] + end + # Similarly for columns, only necessary to get alignments for as many + # columns as could conceivably fit across the screen + maxpossiblecols = div(screenwidth, 1+sepsize) + if n > maxpossiblecols + colsA = [colsA[1:maxpossiblecols]; colsA[(n-maxpossiblecols+1):n]] + end + A = alignment(io, X, rowsA, colsA, screenwidth, screenwidth, sepsize) + # Nine-slicing is accomplished using print_matrix_row repeatedly + if m <= screenheight # rows fit vertically on screen + if n <= length(A) # rows and cols fit so just print whole matrix in one piece + for i in rowsA + print(io, i == first(rowsA) ? pre : presp) + print_matrix_row(io, X,A,i,colsA,sep) + print(io, i == last(rowsA) ? post : postsp) + if i != last(rowsA); println(io); end + end + else # rows fit down screen but cols don't, so need horizontal ellipsis + c = div(screenwidth-length(hdots)+1,2)+1 # what goes to right of ellipsis + Ralign = reverse(alignment(io, X, rowsA, reverse(colsA), c, c, sepsize)) # alignments for right + c = screenwidth - sum(map(sum,Ralign)) - (length(Ralign)-1)*sepsize - length(hdots) + Lalign = alignment(io, X, rowsA, colsA, c, c, sepsize) # alignments for left of ellipsis + for i in rowsA + print(io, i == first(rowsA) ? pre : presp) + print_matrix_row(io, X,Lalign,i,colsA[1:length(Lalign)],sep) + print(io, (i - first(rowsA)) % hmod == 0 ? hdots : repeat(" ", length(hdots))) + print_matrix_row(io, X, Ralign, i, (n - length(Ralign)) .+ colsA, sep) + print(io, i == last(rowsA) ? post : postsp) + if i != last(rowsA); println(io); end + end + end + else # rows don't fit so will need vertical ellipsis + if n <= length(A) # rows don't fit, cols do, so only vertical ellipsis + for i in rowsA + print(io, i == first(rowsA) ? pre : presp) + print_matrix_row(io, X,A,i,colsA,sep) + print(io, i == last(rowsA) ? post : postsp) + if i != rowsA[end] || i == rowsA[halfheight]; println(io); end + if i == rowsA[halfheight] + print(io, i == first(rowsA) ? pre : presp) + print_matrix_vdots(io, vdots,A,sep,vmod,1) + print(io, i == last(rowsA) ? post : postsp * '\n') + end + end + else # neither rows nor cols fit, so use all 3 kinds of dots + c = div(screenwidth-length(hdots)+1,2)+1 + Ralign = reverse(alignment(io, X, rowsA, reverse(colsA), c, c, sepsize)) + c = screenwidth - sum(map(sum,Ralign)) - (length(Ralign)-1)*sepsize - length(hdots) + Lalign = alignment(io, X, rowsA, colsA, c, c, sepsize) + r = mod((length(Ralign)-n+1),vmod) # where to put dots on right half + for i in rowsA + print(io, i == first(rowsA) ? pre : presp) + print_matrix_row(io, X,Lalign,i,colsA[1:length(Lalign)],sep) + print(io, (i - first(rowsA)) % hmod == 0 ? hdots : repeat(" ", length(hdots))) + print_matrix_row(io, X,Ralign,i,(n-length(Ralign)).+colsA,sep) + print(io, i == last(rowsA) ? post : postsp) + if i != rowsA[end] || i == rowsA[halfheight]; println(io); end + if i == rowsA[halfheight] + print(io, i == first(rowsA) ? pre : presp) + print_matrix_vdots(io, vdots,Lalign,sep,vmod,1) + print(io, ddots) + print_matrix_vdots(io, vdots,Ralign,sep,vmod,r) + print(io, i == last(rowsA) ? post : postsp * '\n') + end + end + end + if isempty(rowsA) + print(io, pre) + print(io, vdots) + length(colsA) > 1 && print(io, " ", ddots) + print(io, post) + end + end +end + +# n-dimensional arrays +function show_nd(io::IO, a::AbstractArray, print_matrix, label_slices) + limit::Bool = get(io, :limit, false) + if isempty(a) + return + end + tailinds = tail(tail(indices(a))) + nd = ndims(a)-2 + for I in CartesianRange(tailinds) + idxs = I.I + if limit + for i = 1:nd + ii = idxs[i] + ind = tailinds[i] + if length(ind) > 10 + if ii == ind[4] && all(d->idxs[d]==first(tailinds[d]),1:i-1) + for j=i+1:nd + szj = length(indices(a, j+2)) + indj = tailinds[j] + if szj>10 && first(indj)+2 < idxs[j] <= last(indj)-3 + @goto skip + end + end + #println(io, idxs) + print(io, "...\n\n") + @goto skip + end + if ind[3] < ii <= ind[end-3] + @goto skip + end + end + end + end + if label_slices + print(io, "[:, :, ") + for i = 1:(nd-1); print(io, "$(idxs[i]), "); end + println(io, idxs[end], "] =") + end + slice = view(a, indices(a,1), indices(a,2), idxs...) + print_matrix(io, slice) + print(io, idxs == map(last,tailinds) ? "" : "\n\n") + @label skip + end +end + +""" +`print_matrix_repr(io, X)` prints matrix X with opening and closing square brackets. +""" +function print_matrix_repr(io, X::AbstractArray) + limit = get(io, :limit, false)::Bool + compact, prefix = array_eltype_show_how(X) + if compact && !haskey(io, :compact) + io = IOContext(io, :compact => compact) + end + indr, indc = indices(X,1), indices(X,2) + nr, nc = length(indr), length(indc) + rdots, cdots = false, false + rr1, rr2 = UnitRange{Int}(indr), 1:0 + cr1, cr2 = UnitRange{Int}(indc), 1:0 + if limit + if nr > 4 + rr1, rr2 = rr1[1:2], rr1[nr-1:nr] + rdots = true + end + if nc > 4 + cr1, cr2 = cr1[1:2], cr1[nc-1:nc] + cdots = true + end + end + print(io, prefix, "[") + for rr in (rr1, rr2) + for i in rr + for cr in (cr1, cr2) + for j in cr + j > first(cr) && print(io, " ") + if !isassigned(X,i,j) + print(io, undef_ref_str) + else + el = X[i,j] + show(io, el) + end + end + if last(cr) == last(indc) + i < last(indr) && print(io, "; ") + elseif cdots + print(io, " \u2026 ") + end + end + end + last(rr) != nr && rdots && print(io, "\u2026 ; ") + end + print(io, "]") +end + +show(io::IO, X::AbstractArray) = showarray(io, X, true) + +repremptyarray(io::IO, X::Array{T}) where {T} = print(io, "Array{$T}(", join(size(X),','), ')') +repremptyarray(io, X) = nothing # by default, we don't know this constructor + +function showarray(io::IO, X::AbstractArray, repr::Bool = true; header = true) + if repr && ndims(X) == 1 + return show_vector(io, X, "[", "]") + end + if !haskey(io, :compact) && length(indices(X, 2)) > 1 + io = IOContext(io, :compact => true) + end + if !repr && get(io, :limit, false) && eltype(X) === Method + # override usual show method for Vector{Method}: don't abbreviate long lists + io = IOContext(io, :limit => false) + end + (!repr && header) && summary(io, X) + if !isempty(X) + if !repr && header + print(io, ":") + if get(io, :limit, false) && displaysize(io)[1]-4 <= 0 + return print(io, " …") + else + println(io) + end + end + if ndims(X) == 0 + if isassigned(X) + return show(io, X[]) + else + return print(io, undef_ref_str) + end + end + if repr + if ndims(X) <= 2 + print_matrix_repr(io, X) + else + show_nd(io, X, print_matrix_repr, false) + end + else + punct = (" ", " ", "") + if ndims(X) <= 2 + print_matrix(io, X, punct...) + else + show_nd(io, X, + (io, slice) -> print_matrix(io, slice, punct...), + !repr) + end + end + elseif repr + repremptyarray(io, X) + end +end + +# returns compact, prefix +function array_eltype_show_how(X) + e = eltype(X) + if print_without_params(e) + str = string(unwrap_unionall(e).name) # Print "Array" rather than "Array{T,N}" + else + str = string(e) + end + # Types hard-coded here are those which are created by default for a given syntax + (_isleaftype(e), + (!isempty(X) && (e===Float64 || e===Int || e===Char || e===String) ? "" : str)) +end + +function show_vector(io::IO, v, opn, cls) + compact, prefix = array_eltype_show_how(v) + limited = get(io, :limit, false) + if compact && !haskey(io, :compact) + io = IOContext(io, :compact => compact) + end + print(io, prefix) + if limited && _length(v) > 20 + inds = indices1(v) + show_delim_array(io, v, opn, ",", "", false, inds[1], inds[1]+9) + print(io, " \u2026 ") + show_delim_array(io, v, "", ",", cls, false, inds[end-9], inds[end]) + else + show_delim_array(io, v, opn, ",", cls, false) + end +end diff --git a/base/show.jl b/base/show.jl index 65de35087fe25..ddf24021157ee 100644 --- a/base/show.jl +++ b/base/show.jl @@ -1610,222 +1610,7 @@ function alignment(io::IO, x::Pair) end const undef_ref_str = "#undef" -const undef_ref_alignment = (3,3) -""" -`alignment(X, rows, cols, cols_if_complete, cols_otherwise, sep)` returns the -alignment for specified parts of array `X`, returning the (left,right) info. -It will look in X's `rows`, `cols` (both lists of indices) -and figure out what's needed to be fully aligned, for example looking all -the way down a column and finding out the maximum size of each element. -Parameter `sep::Integer` is number of spaces to put between elements. -`cols_if_complete` and `cols_otherwise` indicate screen width to use. -Alignment is reported as a vector of (left,right) tuples, one for each -column going across the screen. -""" -function alignment(io::IO, X::AbstractVecOrMat, - rows::AbstractVector, cols::AbstractVector, - cols_if_complete::Integer, cols_otherwise::Integer, sep::Integer) - a = Tuple{Int, Int}[] - for j in cols # need to go down each column one at a time - l = r = 0 - for i in rows # plumb down and see what largest element sizes are - if isassigned(X,i,j) - aij = alignment(io, X[i,j]) - else - aij = undef_ref_alignment - end - l = max(l, aij[1]) # left characters - r = max(r, aij[2]) # right characters - end - push!(a, (l, r)) # one tuple per column of X, pruned to screen width - if length(a) > 1 && sum(map(sum,a)) + sep*length(a) >= cols_if_complete - pop!(a) # remove this latest tuple if we're already beyond screen width - break - end - end - if 1 < length(a) < length(indices(X,2)) - while sum(map(sum,a)) + sep*length(a) >= cols_otherwise - pop!(a) - end - end - return a -end - -""" -Unexported convenience function used in body of `replace_in_print_matrix` -methods. By default returns a string of the same width as original with a -centered cdot, used in printing of structural zeros of structured matrices. -Accept keyword args `c` for alternate single character marker. -""" -function replace_with_centered_mark(s::AbstractString;c::Char = '⋅') - N = length(s) - return join(setindex!([" " for i=1:N],string(c),ceil(Int,N/2))) -end - -""" -`print_matrix_row(io, X, A, i, cols, sep)` produces the aligned output for -a single matrix row X[i, cols] where the desired list of columns is given. -The corresponding alignment A is used, and the separation between elements -is specified as string sep. -`print_matrix_row` will also respect compact output for elements. -""" -function print_matrix_row(io::IO, - X::AbstractVecOrMat, A::Vector, - i::Integer, cols::AbstractVector, sep::AbstractString) - isempty(A) || first(indices(cols,1)) == 1 || throw(DimensionMismatch("indices of cols ($(indices(cols,1))) must start at 1")) - for k = 1:length(A) - j = cols[k] - if isassigned(X,Int(i),Int(j)) # isassigned accepts only `Int` indices - x = X[i,j] - a = alignment(io, x) - sx = sprint(0, show, x, env=io) - else - a = undef_ref_alignment - sx = undef_ref_str - end - l = repeat(" ", A[k][1]-a[1]) # pad on left and right as needed - r = repeat(" ", A[k][2]-a[2]) - prettysx = replace_in_print_matrix(X,i,j,sx) - print(io, l, prettysx, r) - if k < length(A); print(io, sep); end - end -end - -""" -`print_matrix_vdots` is used to show a series of vertical ellipsis instead -of a bunch of rows for long matrices. Not only is the string vdots shown -but it also repeated every M elements if desired. -""" -function print_matrix_vdots(io::IO, vdots::AbstractString, - A::Vector, sep::AbstractString, M::Integer, m::Integer) - for k = 1:length(A) - w = A[k][1] + A[k][2] - if k % M == m - l = repeat(" ", max(0, A[k][1]-length(vdots))) - r = repeat(" ", max(0, w-length(vdots)-length(l))) - print(io, l, vdots, r) - else - print(io, repeat(" ", w)) - end - if k < length(A); print(io, sep); end - end -end - -""" - print_matrix(io::IO, mat, pre, sep, post, hdots, vdots, ddots, hmod, vmod) - -Prints a matrix with limited output size. If `io` sets `:limit` to true, -then only the corners of the matrix are printed, separated with vertical, -horizontal, and diagonal ellipses as appropriate. -Optional arguments are string pre (printed before the matrix, e.g. an opening bracket) -which will cause a corresponding same-size indent on following rows, and -string post (printed at the end of the last row of the matrix). -Also options to use different ellipsis characters hdots, vdots, ddots. -These are repeated every hmod or vmod elements. -""" -function print_matrix(io::IO, X::AbstractVecOrMat, - pre::AbstractString = " ", # pre-matrix string - sep::AbstractString = " ", # separator between elements - post::AbstractString = "", # post-matrix string - hdots::AbstractString = " \u2026 ", - vdots::AbstractString = "\u22ee", - ddots::AbstractString = " \u22f1 ", - hmod::Integer = 5, vmod::Integer = 5) - if !get(io, :limit, false) - screenheight = screenwidth = typemax(Int) - else - sz = displaysize(io) - screenheight, screenwidth = sz[1] - 4, sz[2] - end - screenwidth -= length(pre) + length(post) - presp = repeat(" ", length(pre)) # indent each row to match pre string - postsp = "" - @assert textwidth(hdots) == textwidth(ddots) - sepsize = length(sep) - rowsA, colsA = indices(X,1), indices(X,2) - m, n = length(rowsA), length(colsA) - # To figure out alignments, only need to look at as many rows as could - # fit down screen. If screen has at least as many rows as A, look at A. - # If not, then we only need to look at the first and last chunks of A, - # each half a screen height in size. - halfheight = div(screenheight,2) - if m > screenheight - rowsA = [rowsA[1:halfheight]; rowsA[m-div(screenheight-1,2)+1:m]] - end - # Similarly for columns, only necessary to get alignments for as many - # columns as could conceivably fit across the screen - maxpossiblecols = div(screenwidth, 1+sepsize) - if n > maxpossiblecols - colsA = [colsA[1:maxpossiblecols]; colsA[(n-maxpossiblecols+1):n]] - end - A = alignment(io, X, rowsA, colsA, screenwidth, screenwidth, sepsize) - # Nine-slicing is accomplished using print_matrix_row repeatedly - if m <= screenheight # rows fit vertically on screen - if n <= length(A) # rows and cols fit so just print whole matrix in one piece - for i in rowsA - print(io, i == first(rowsA) ? pre : presp) - print_matrix_row(io, X,A,i,colsA,sep) - print(io, i == last(rowsA) ? post : postsp) - if i != last(rowsA); println(io); end - end - else # rows fit down screen but cols don't, so need horizontal ellipsis - c = div(screenwidth-length(hdots)+1,2)+1 # what goes to right of ellipsis - Ralign = reverse(alignment(io, X, rowsA, reverse(colsA), c, c, sepsize)) # alignments for right - c = screenwidth - sum(map(sum,Ralign)) - (length(Ralign)-1)*sepsize - length(hdots) - Lalign = alignment(io, X, rowsA, colsA, c, c, sepsize) # alignments for left of ellipsis - for i in rowsA - print(io, i == first(rowsA) ? pre : presp) - print_matrix_row(io, X,Lalign,i,colsA[1:length(Lalign)],sep) - print(io, (i - first(rowsA)) % hmod == 0 ? hdots : repeat(" ", length(hdots))) - print_matrix_row(io, X, Ralign, i, (n - length(Ralign)) .+ colsA, sep) - print(io, i == last(rowsA) ? post : postsp) - if i != last(rowsA); println(io); end - end - end - else # rows don't fit so will need vertical ellipsis - if n <= length(A) # rows don't fit, cols do, so only vertical ellipsis - for i in rowsA - print(io, i == first(rowsA) ? pre : presp) - print_matrix_row(io, X,A,i,colsA,sep) - print(io, i == last(rowsA) ? post : postsp) - if i != rowsA[end] || i == rowsA[halfheight]; println(io); end - if i == rowsA[halfheight] - print(io, i == first(rowsA) ? pre : presp) - print_matrix_vdots(io, vdots,A,sep,vmod,1) - print(io, i == last(rowsA) ? post : postsp * '\n') - end - end - else # neither rows nor cols fit, so use all 3 kinds of dots - c = div(screenwidth-length(hdots)+1,2)+1 - Ralign = reverse(alignment(io, X, rowsA, reverse(colsA), c, c, sepsize)) - c = screenwidth - sum(map(sum,Ralign)) - (length(Ralign)-1)*sepsize - length(hdots) - Lalign = alignment(io, X, rowsA, colsA, c, c, sepsize) - r = mod((length(Ralign)-n+1),vmod) # where to put dots on right half - for i in rowsA - print(io, i == first(rowsA) ? pre : presp) - print_matrix_row(io, X,Lalign,i,colsA[1:length(Lalign)],sep) - print(io, (i - first(rowsA)) % hmod == 0 ? hdots : repeat(" ", length(hdots))) - print_matrix_row(io, X,Ralign,i,(n-length(Ralign)).+colsA,sep) - print(io, i == last(rowsA) ? post : postsp) - if i != rowsA[end] || i == rowsA[halfheight]; println(io); end - if i == rowsA[halfheight] - print(io, i == first(rowsA) ? pre : presp) - print_matrix_vdots(io, vdots,Lalign,sep,vmod,1) - print(io, ddots) - print_matrix_vdots(io, vdots,Ralign,sep,vmod,r) - print(io, i == last(rowsA) ? post : postsp * '\n') - end - end - end - if isempty(rowsA) - print(io, pre) - print(io, vdots) - length(colsA) > 1 && print(io, " ", ddots) - print(io, post) - end - end -end """ summary(io::IO, x) @@ -1954,154 +1739,6 @@ function showarg(io::IO, r::ReinterpretArray{T}, toplevel) where {T} print(io, ')') end -# n-dimensional arrays -function show_nd(io::IO, a::AbstractArray, print_matrix, label_slices) - limit::Bool = get(io, :limit, false) - if isempty(a) - return - end - tailinds = tail(tail(indices(a))) - nd = ndims(a)-2 - for I in CartesianRange(tailinds) - idxs = I.I - if limit - for i = 1:nd - ii = idxs[i] - ind = tailinds[i] - if length(ind) > 10 - if ii == ind[4] && all(d->idxs[d]==first(tailinds[d]),1:i-1) - for j=i+1:nd - szj = length(indices(a, j+2)) - indj = tailinds[j] - if szj>10 && first(indj)+2 < idxs[j] <= last(indj)-3 - @goto skip - end - end - #println(io, idxs) - print(io, "...\n\n") - @goto skip - end - if ind[3] < ii <= ind[end-3] - @goto skip - end - end - end - end - if label_slices - print(io, "[:, :, ") - for i = 1:(nd-1); print(io, "$(idxs[i]), "); end - println(io, idxs[end], "] =") - end - slice = view(a, indices(a,1), indices(a,2), idxs...) - print_matrix(io, slice) - print(io, idxs == map(last,tailinds) ? "" : "\n\n") - @label skip - end -end - -""" -`print_matrix_repr(io, X)` prints matrix X with opening and closing square brackets. -""" -function print_matrix_repr(io, X::AbstractArray) - limit = get(io, :limit, false)::Bool - compact, prefix = array_eltype_show_how(X) - if compact && !haskey(io, :compact) - io = IOContext(io, :compact => compact) - end - indr, indc = indices(X,1), indices(X,2) - nr, nc = length(indr), length(indc) - rdots, cdots = false, false - rr1, rr2 = UnitRange{Int}(indr), 1:0 - cr1, cr2 = UnitRange{Int}(indc), 1:0 - if limit - if nr > 4 - rr1, rr2 = rr1[1:2], rr1[nr-1:nr] - rdots = true - end - if nc > 4 - cr1, cr2 = cr1[1:2], cr1[nc-1:nc] - cdots = true - end - end - print(io, prefix, "[") - for rr in (rr1, rr2) - for i in rr - for cr in (cr1, cr2) - for j in cr - j > first(cr) && print(io, " ") - if !isassigned(X,i,j) - print(io, undef_ref_str) - else - el = X[i,j] - show(io, el) - end - end - if last(cr) == last(indc) - i < last(indr) && print(io, "; ") - elseif cdots - print(io, " \u2026 ") - end - end - end - last(rr) != nr && rdots && print(io, "\u2026 ; ") - end - print(io, "]") -end - -show(io::IO, X::AbstractArray) = showarray(io, X, true) - -repremptyarray(io::IO, X::Array{T}) where {T} = print(io, "Array{$T}(", join(size(X),','), ')') -repremptyarray(io, X) = nothing # by default, we don't know this constructor - -function showarray(io::IO, X::AbstractArray, repr::Bool = true; header = true) - if repr && ndims(X) == 1 - return show_vector(io, X, "[", "]") - end - if !haskey(io, :compact) && length(indices(X, 2)) > 1 - io = IOContext(io, :compact => true) - end - if !repr && get(io, :limit, false) && eltype(X) === Method - # override usual show method for Vector{Method}: don't abbreviate long lists - io = IOContext(io, :limit => false) - end - (!repr && header) && summary(io, X) - if !isempty(X) - if !repr && header - print(io, ":") - if get(io, :limit, false) && displaysize(io)[1]-4 <= 0 - return print(io, " …") - else - println(io) - end - end - if ndims(X) == 0 - if isassigned(X) - return show(io, X[]) - else - return print(io, undef_ref_str) - end - end - if repr - if ndims(X) <= 2 - print_matrix_repr(io, X) - else - show_nd(io, X, print_matrix_repr, false) - end - else - punct = (" ", " ", "") - if ndims(X) <= 2 - print_matrix(io, X, punct...) - else - show_nd(io, X, - (io, slice) -> print_matrix(io, slice, punct...), - !repr) - end - end - elseif repr - repremptyarray(io, X) - end -end - """ showcompact(x) showcompact(io::IO, x) @@ -2136,35 +1773,6 @@ function showcompact(io::IO, x) end end -# returns compact, prefix -function array_eltype_show_how(X) - e = eltype(X) - if print_without_params(e) - str = string(unwrap_unionall(e).name) # Print "Array" rather than "Array{T,N}" - else - str = string(e) - end - # Types hard-coded here are those which are created by default for a given syntax - (_isleaftype(e), - (!isempty(X) && (e===Float64 || e===Int || e===Char || e===String) ? "" : str)) -end - -function show_vector(io::IO, v, opn, cls) - compact, prefix = array_eltype_show_how(v) - limited = get(io, :limit, false) - if compact && !haskey(io, :compact) - io = IOContext(io, :compact => compact) - end - print(io, prefix) - if limited && _length(v) > 20 - inds = indices1(v) - show_delim_array(io, v, opn, ",", "", false, inds[1], inds[1]+9) - print(io, " \u2026 ") - show_delim_array(io, v, "", ",", cls, false, inds[end-9], inds[end]) - else - show_delim_array(io, v, opn, ",", cls, false) - end -end # printing BitArrays diff --git a/base/sysimg.jl b/base/sysimg.jl index 96b083c1af3fc..7204750b00d85 100644 --- a/base/sysimg.jl +++ b/base/sysimg.jl @@ -229,6 +229,7 @@ include("parse.jl") include("shell.jl") include("regex.jl") include("show.jl") +include("arrayshow.jl") # multidimensional arrays include("cartesian.jl") From fe6709bfdfaf6650a2a62d13da38378e4b3c1710 Mon Sep 17 00:00:00 2001 From: Rafael Fourquet Date: Sun, 8 Oct 2017 12:17:00 +0200 Subject: [PATCH 2/2] IOContext: introduce the :typeinfo property Before, the :compact property was conflating 2 concepts: 1) are we low on screen space? 2) can we skip printing individual type information for elements in a collection? Now, :compact controls only 1), while :typeinfo controls 2). Cf. #22981 for context and discussion. Credit to Stefan Karpinski for the formulation of the design implemented here. --- base/arrayshow.jl | 244 ++++++++++++++++++++++++++++++-------------- base/grisu/grisu.jl | 12 ++- base/precompile.jl | 6 +- base/replutil.jl | 4 +- base/set.jl | 11 +- base/show.jl | 9 +- examples/ModInts.jl | 2 +- test/nullable.jl | 11 -- test/repl.jl | 1 + test/sets.jl | 2 +- test/show.jl | 27 ++++- 11 files changed, 217 insertions(+), 112 deletions(-) diff --git a/base/arrayshow.jl b/base/arrayshow.jl index 38ae8c280274e..9f16782f76e88 100644 --- a/base/arrayshow.jl +++ b/base/arrayshow.jl @@ -1,3 +1,38 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +# methods related to array printing + +# Printing a value requires to take into account the :typeinfo property +# from the IO context; this property encodes (as a type) the type information +# that is supposed to have already been displayed concerning this value, +# so that redundancy can be avoided. For example, when printing an array of +# `Float16` values, the header "Float16" will be printed, and the values +# can simply be printed with the decimal representations: +# show(Float16(1)) -> "Float16(1.0)" +# show([Float16(1)]) -> "Float16[1.0]" (instead of "Float16[Float16(1.0)]") +# Similarly: +# show([[Float16(1)]]) -> "Array{Float16}[[1.0]]" (instead of "Array{Float16}[Float16[1.0]]") +# +# The array printing methods here can be grouped into two categories (and are annotated as such): +# 1) "typeinfo aware" : these are "API boundaries" functions, which will read the typeinfo +# property from the context, and pass down to their value an updated property +# according to its eltype; at each layer of nesting, only one "typeinfo aware" +# function must be called; +# 2) "typeinfo agnostic": these are helper functions used by the first category; hence +# they don't manipulate the typeinfo property, and let the printing routines +# for their elements read directly the property set by their callers +# +# Non-annotated functions are even lower level (e.g. print_matrix_row), so they fall +# by default into category 2. +# +# The basic organization of this file is +# 1) printing with `display` +# 2) printing with `show` +# 3) Logic for displaying type information + + +## printing with `display` + """ Unexported convenience function used in body of `replace_in_print_matrix` methods. By default returns a string of the same width as original with a @@ -101,6 +136,7 @@ function print_matrix_vdots(io::IO, vdots::AbstractString, end end +# typeinfo agnostic """ print_matrix(io::IO, mat, pre, sep, post, hdots, vdots, ddots, hmod, vmod) @@ -216,8 +252,9 @@ function print_matrix(io::IO, X::AbstractVecOrMat, end end +# typeinfo agnostic # n-dimensional arrays -function show_nd(io::IO, a::AbstractArray, print_matrix, label_slices) +function show_nd(io::IO, a::AbstractArray, print_matrix::Function, label_slices::Bool) limit::Bool = get(io, :limit, false) if isempty(a) return @@ -261,15 +298,63 @@ function show_nd(io::IO, a::AbstractArray, print_matrix, label_slices) end end +# print_array: main helper functions for _display +# typeinfo agnostic + +# 0-dimensional arrays +print_array(io::IO, X::AbstractArray{T,0} where T) = + isassigned(X) ? show(io, X[]) : + print(io, undef_ref_str) + +print_array(io::IO, X::AbstractVecOrMat) = print_matrix(io, X) + +print_array(io::IO, X::AbstractArray) = show_nd(io, X, print_matrix, true) + +# typeinfo aware +# implements: show(io::IO, ::MIME"text/plain", X::AbstractArray) +function _display(io::IO, X::AbstractArray) + # 0) compute new IOContext + if !haskey(io, :compact) && length(indices(X, 2)) > 1 + io = IOContext(io, :compact => true) + end + if get(io, :limit, false) && eltype(X) === Method + # override usual show method for Vector{Method}: don't abbreviate long lists + io = IOContext(io, :limit => false) + end + # we assume this function is always called from top-level, i.e. that it's not nested + # within another "show" method; hence we always print the summary, without + # checking for current :typeinfo (this could be changed in the future) + io = IOContext(io, :typeinfo => eltype(X)) + + # 1) print summary info + summary(io, X) + isempty(X) && return + print(io, ":") + if get(io, :limit, false) && displaysize(io)[1]-4 <= 0 + return print(io, " …") + else + println(io) + end + + # 2) show actual content + print_array(io, X) +end + + +## printing with `show` + +### non-Vector arrays + +# _show_nonempty & _show_empty: main helper functions for show(io, X) +# typeinfo agnostic + """ -`print_matrix_repr(io, X)` prints matrix X with opening and closing square brackets. +`_show_nonempty(io, X::AbstractMatrix, prefix)` prints matrix X with opening and closing square brackets, +preceded by `prefix`, supposed to encode the type of the elements. """ -function print_matrix_repr(io, X::AbstractArray) +function _show_nonempty(io::IO, X::AbstractMatrix, prefix::String) + @assert !isempty(X) limit = get(io, :limit, false)::Bool - compact, prefix = array_eltype_show_how(X) - if compact && !haskey(io, :compact) - io = IOContext(io, :compact => compact) - end indr, indc = indices(X,1), indices(X,2) nr, nc = length(indr), length(indc) rdots, cdots = false, false @@ -310,86 +395,93 @@ function print_matrix_repr(io, X::AbstractArray) print(io, "]") end -show(io::IO, X::AbstractArray) = showarray(io, X, true) -repremptyarray(io::IO, X::Array{T}) where {T} = print(io, "Array{$T}(", join(size(X),','), ')') -repremptyarray(io, X) = nothing # by default, we don't know this constructor +_show_nonempty(io::IO, X::AbstractArray, prefix::String) = + show_nd(io, X, (io, slice) -> _show_nonempty(io, slice, prefix), false) -function showarray(io::IO, X::AbstractArray, repr::Bool = true; header = true) - if repr && ndims(X) == 1 - return show_vector(io, X, "[", "]") - end - if !haskey(io, :compact) && length(indices(X, 2)) > 1 - io = IOContext(io, :compact => true) - end - if !repr && get(io, :limit, false) && eltype(X) === Method - # override usual show method for Vector{Method}: don't abbreviate long lists - io = IOContext(io, :limit => false) - end - (!repr && header) && summary(io, X) - if !isempty(X) - if !repr && header - print(io, ":") - if get(io, :limit, false) && displaysize(io)[1]-4 <= 0 - return print(io, " …") - else - println(io) - end - end - if ndims(X) == 0 - if isassigned(X) - return show(io, X[]) - else - return print(io, undef_ref_str) - end - end - if repr - if ndims(X) <= 2 - print_matrix_repr(io, X) - else - show_nd(io, X, print_matrix_repr, false) - end - else - punct = (" ", " ", "") - if ndims(X) <= 2 - print_matrix(io, X, punct...) - else - show_nd(io, X, - (io, slice) -> print_matrix(io, slice, punct...), - !repr) - end - end - elseif repr - repremptyarray(io, X) - end -end +# a specific call path is used to show vectors (show_vector) +_show_nonempty(::IO, ::AbstractVector, ::String) = + error("_show_nonempty(::IO, ::AbstractVector, ::String) is not implemented") -# returns compact, prefix -function array_eltype_show_how(X) - e = eltype(X) - if print_without_params(e) - str = string(unwrap_unionall(e).name) # Print "Array" rather than "Array{T,N}" - else - str = string(e) - end - # Types hard-coded here are those which are created by default for a given syntax - (_isleaftype(e), - (!isempty(X) && (e===Float64 || e===Int || e===Char || e===String) ? "" : str)) +_show_nonempty(io::IO, X::AbstractArray{T,0} where T, prefix::String) = print_array(io, X) + +# NOTE: it's not clear how this method could use the :typeinfo attribute +_show_empty(io::IO, X::Array{T}) where {T} = print(io, "Array{$T}(", join(size(X),','), ')') +_show_empty(io, X) = nothing # by default, we don't know this constructor + +# typeinfo aware (necessarily) +function show(io::IO, X::AbstractArray) + @assert ndims(X) != 1 + prefix = typeinfo_prefix(io, X) + io = IOContext(io, :typeinfo => eltype(X), :compact => true) + isempty(X) ? + _show_empty(io, X) : + _show_nonempty(io, X, prefix) end -function show_vector(io::IO, v, opn, cls) - compact, prefix = array_eltype_show_how(v) +### Vector arrays + +# typeinfo aware +# NOTE: v is not constrained to be a vector, as this function can work with iterables +# in general (it's used e.g. by show(::IO, ::Set)) +function show_vector(io::IO, v, opn='[', cls=']') + print(io, typeinfo_prefix(io, v)) + # directly or indirectly, the context now knows about eltype(v) + io = IOContext(io, :typeinfo => eltype(v), :compact => true) limited = get(io, :limit, false) - if compact && !haskey(io, :compact) - io = IOContext(io, :compact => compact) - end - print(io, prefix) if limited && _length(v) > 20 inds = indices1(v) show_delim_array(io, v, opn, ",", "", false, inds[1], inds[1]+9) - print(io, " \u2026 ") + print(io, " … ") show_delim_array(io, v, "", ",", cls, false, inds[end-9], inds[end]) else show_delim_array(io, v, opn, ",", cls, false) end end + +show(io::IO, X::AbstractVector) = show_vector(io, X) + + +## Logic for displaying type information + +# given type `typeinfo` extracted from context, assuming a collection +# is being displayed, deduce the elements type; in spirit this is +# similar to `eltype`, but in some cases this would lead to incomplete +# information: assume we are at the top level, and no typeinfo is set, +# and that it is deduced to be typeinfo=Any by default, and consider +# printing X = Any[1]; to know if the eltype of X is already displayed, +# we would compare eltype(X) to eltype(typeinfo) == Any, and deduce +# that we don't need to print X's eltype because it's already known by +# the context, which is wrong; even if default value of typeinfo is +# not set to Any, then the problem would be similar one layer below +# when printing an array like Any[Any[1]]; hence we must treat Any +# specially +function typeinfo_eltype(typeinfo::Type)::Union{Type,Void} + if typeinfo == Any + # the current context knows nothing about what is being displayed, not even + # whether it's a collection or scalar + nothing + else + # we assume typeinfo refers to a collection-like type, whose + # eltype meaningfully represents what the context knows about + # the eltype of the object currently being displayed + eltype(typeinfo) + end +end + +# X not constrained, can be any iterable (cf. show_vector) +function typeinfo_prefix(io::IO, X) + typeinfo = get(io, :typeinfo, Any)::Type + @assert X isa typeinfo "$(typeof(X)) is not a subtype of $typeinfo" + # what the context already knows about the eltype of X: + eltype_ctx = typeinfo_eltype(typeinfo) + eltype_X = eltype(X) + # Types hard-coded here are those which are created by default for a given syntax + if eltype_X == eltype_ctx || !isempty(X) && eltype_X in (Float64, Int, Char, String) + "" + elseif print_without_params(eltype_X) + string(unwrap_unionall(eltype_X).name) # Print "Array" rather than "Array{T,N}" + else + string(eltype_X) + end +end diff --git a/base/grisu/grisu.jl b/base/grisu/grisu.jl index 72748e1701638..953b386514106 100644 --- a/base/grisu/grisu.jl +++ b/base/grisu/grisu.jl @@ -118,15 +118,21 @@ function Base.show(io::IO, x::Union{Float64,Float32}) if get(io, :compact, false) _show(io, x, PRECISION, 6, x isa Float64, true) else - _show(io, x, SHORTEST, 0, true, false) + _show(io, x, SHORTEST, 0, get(io, :typeinfo, Any) !== typeof(x), false) end end function Base.show(io::IO, x::Float16) - if get(io, :compact, false) + hastypeinfo = Float16 === get(io, :typeinfo, Any) + # if hastypeinfo, the printing would be more compact using `SHORTEST` + # while still retaining all the information + # BUT: we want to print all digits in `show`, not in display, so we rely + # on the :compact property to make the decision + # (cf. https://github.com/JuliaLang/julia/pull/24651#issuecomment-345535687) + if get(io, :compact, false) && !hastypeinfo _show(io, x, PRECISION, 5, false, true) else - _show(io, x, SHORTEST, 0, true, false) + _show(io, x, SHORTEST, 0, !hastypeinfo, false) end end diff --git a/base/precompile.jl b/base/precompile.jl index d191f3dac0a73..f0be54c9270af 100644 --- a/base/precompile.jl +++ b/base/precompile.jl @@ -1596,7 +1596,6 @@ precompile(Tuple{typeof(Base.indexed_next), Tuple{Array{Int64, 1}, Void}, Int64, precompile(Tuple{typeof(Base.setdiff), Array{Int64, 1}, Array{Int64, 1}}) precompile(Tuple{typeof(Base.Multimedia.display), Array{Int64, 1}}) precompile(Tuple{typeof(Base.isassigned), Array{Int64, 1}, Int64}) -precompile(Tuple{typeof(Base.array_eltype_show_how), Array{Int64, 1}}) precompile(Tuple{typeof(Base.summary), Array{Int64, 1}, Tuple{Base.OneTo{Int64}}}) precompile(Tuple{typeof(Base.isassigned), Array{Int64, 1}, Int64, Int64}) precompile(Tuple{typeof(Base.isassigned), Array{Int64, 1}}) @@ -1608,9 +1607,8 @@ precompile(Tuple{typeof(Base.print), Base.IOContext{Base.Terminals.TTYTerminal}, precompile(Tuple{typeof(Base.print), Base.IOContext{Base.Terminals.TTYTerminal}, String, String}) precompile(Tuple{typeof(Base.print), Base.IOContext{Base.Terminals.TTYTerminal}, String, String, Char}) precompile(Tuple{typeof(Base.show_vector), Base.IOContext{Base.Terminals.TTYTerminal}, Array{Int64, 1}, String, String}) -precompile(Tuple{typeof(Base.print_matrix_repr), Base.IOContext{Base.Terminals.TTYTerminal}, Array{Int64, 1}}) -precompile(Tuple{typeof(Base.show_nd), Base.IOContext{Base.Terminals.TTYTerminal}, Array{Int64, 1}, typeof(Base.print_matrix_repr), Bool}) -precompile(Tuple{typeof(Base.repremptyarray), Base.IOContext{Base.Terminals.TTYTerminal}, Array{Int64, 1}}) +precompile(Tuple{typeof(Base._show_nonempty), Base.IOContext{Base.Terminals.TTYTerminal}, Array{Int64, 1}, String}) +precompile(Tuple{typeof(Base._show_empty), Base.IOContext{Base.Terminals.TTYTerminal}, Array{Int64, 1}}) precompile(Tuple{typeof(Base.print_matrix), Base.IOContext{Base.Terminals.TTYTerminal}, Array{Int64, 1}, String, String, String}) precompile(Tuple{typeof(Base.getindex), Base.ImmutableDict{Symbol, Any}, Symbol}) precompile(Tuple{typeof(Base.vcat), Base.OneTo{Int64}}) diff --git a/base/replutil.jl b/base/replutil.jl index 3530ac9f3aaf3..65a72b0864007 100644 --- a/base/replutil.jl +++ b/base/replutil.jl @@ -136,7 +136,7 @@ function show(io::IO, ::MIME"text/plain", t::Task) end end -show(io::IO, ::MIME"text/plain", X::AbstractArray) = showarray(io, X, false) +show(io::IO, ::MIME"text/plain", X::AbstractArray) = _display(io, X) show(io::IO, ::MIME"text/plain", r::AbstractRange) = show(io, r) # always use the compact form for printing ranges # display something useful even for strings containing arbitrary @@ -146,7 +146,7 @@ function show(io::IO, ::MIME"text/plain", s::String) show(io, s) else println(io, sizeof(s), "-byte String of invalid UTF-8 data:") - showarray(io, Vector{UInt8}(s), false; header=false) + print_array(io, Vector{UInt8}(s)) end end diff --git a/base/set.jl b/base/set.jl index e75a41adcc419..dea43bc4b665f 100644 --- a/base/set.jl +++ b/base/set.jl @@ -29,14 +29,9 @@ similar(s::Set{T}) where {T} = Set{T}() similar(s::Set, T::Type) = Set{T}() function show(io::IO, s::Set) - print(io, "Set") - if isempty(s) - print(io, "{", eltype(s), "}()") - return - end - print(io, "(") - show_vector(io, s, "[", "]") - print(io, ")") + print(io, "Set(") + show_vector(io, s) + print(io, ')') end isempty(s::Set) = isempty(s.dict) diff --git a/base/show.jl b/base/show.jl index ddf24021157ee..15ab70c465584 100644 --- a/base/show.jl +++ b/base/show.jl @@ -61,6 +61,12 @@ The following properties are in common use: - `:displaysize`: A `Tuple{Int,Int}` giving the size in rows and columns to use for text output. This can be used to override the display size for called functions, but to get the size of the screen use the `displaysize` function. + - `:typeinfo`: a `Type` characterizing the information already printed + concerning the type of the object about to be displayed. This is mainly useful when + displaying a collection of objects of the same type, so that redundant type information + can be avoided (e.g. `[Float16(0)]` can be shown as "Float16[0.0]" instead + of "Float16[Float16(0.0)]" : while displaying the elements of the array, the `:typeinfo` + property will be set to `Float16`). # Examples ```jldoctest @@ -133,7 +139,8 @@ function show_default(io::IO, @nospecialize(x)) nb = sizeof(x) if nf != 0 || nb == 0 if !show_circular(io, x) - recur_io = IOContext(io, Pair{Symbol,Any}(:SHOWN_SET, x)) + recur_io = IOContext(io, Pair{Symbol,Any}(:SHOWN_SET, x), + Pair{Symbol,Any}(:typeinfo, Any)) for i in 1:nf f = fieldname(t, i) if !isdefined(x, f) diff --git a/examples/ModInts.jl b/examples/ModInts.jl index 59fde002d26c5..01fa574a4188a 100644 --- a/examples/ModInts.jl +++ b/examples/ModInts.jl @@ -11,7 +11,7 @@ struct ModInt{n} <: Integer end Base.show(io::IO, k::ModInt{n}) where {n} = - print(io, get(io, :compact, false) ? k.k : "$(k.k) mod $n") + print(io, get(io, :typeinfo, Any) == typeof(k) ? k.k : "$(k.k) mod $n") (+)(a::ModInt{n}, b::ModInt{n}) where {n} = ModInt{n}(a.k+b.k) (-)(a::ModInt{n}, b::ModInt{n}) where {n} = ModInt{n}(a.k-b.k) diff --git a/test/nullable.jl b/test/nullable.jl index b3f8b82c7d95e..2edc67cdc3cae 100644 --- a/test/nullable.jl +++ b/test/nullable.jl @@ -101,17 +101,6 @@ for (i, T) in enumerate(types) show(io1, x3) showcompact(io2, get(x3)) @test String(take!(io1)) == @sprintf("Nullable{%s}(%s)", T, String(take!(io2))) - - a1 = [x2] - show(IOContext(io1, :compact => false), a1) - show(IOContext(io2, :compact => false), x2) - @test String(take!(io1)) == - @sprintf("Nullable{%s}[%s]", string(T), String(take!(io2))) - - show(io1, a1) - show(IOContext(io2, :compact => true), x2) - @test String(take!(io1)) == - @sprintf("Nullable{%s}[%s]", string(T), String(take!(io2))) end module NullableTestEnum diff --git a/test/repl.jl b/test/repl.jl index b2caadca48bcd..2d15195a37d21 100644 --- a/test/repl.jl +++ b/test/repl.jl @@ -166,6 +166,7 @@ fake_repl() do stdin_write, stdout_read, repl redirect_stdout(old_stdout) end close(proc_stdout) + @test contains(wait(get_stdout), "HI\n") @test wait(get_stdout) == "HI\n" end diff --git a/test/sets.jl b/test/sets.jl index af5a664b7b606..ef46d7fec7564 100644 --- a/test/sets.jl +++ b/test/sets.jl @@ -73,7 +73,7 @@ end @test ===(eltype(s3), Float32) end @testset "show" begin - @test sprint(show, Set()) == "Set{Any}()" + @test sprint(show, Set()) == "Set(Any[])" @test sprint(show, Set(['a'])) == "Set(['a'])" end @testset "isempty, length, in, push, pop, delete" begin diff --git a/test/show.jl b/test/show.jl index b78744ed3fa4a..d71c53be11de3 100644 --- a/test/show.jl +++ b/test/show.jl @@ -4,11 +4,13 @@ include("testenv.jl") replstr(x) = sprint((io,x) -> show(IOContext(io, :limit => true, :displaysize => (24, 80)), MIME("text/plain"), x), x) +showstr(x) = sprint((io,x) -> show(IOContext(io, :limit => true, :displaysize => (24, 80)), x), x) + @test replstr(Array{Any}(uninitialized, 2)) == "2-element Array{Any,1}:\n #undef\n #undef" @test replstr(Array{Any}(uninitialized, 2,2)) == "2×2 Array{Any,2}:\n #undef #undef\n #undef #undef" @test replstr(Array{Any}(uninitialized, 2,2,2)) == "2×2×2 Array{Any,3}:\n[:, :, 1] =\n #undef #undef\n #undef #undef\n\n[:, :, 2] =\n #undef #undef\n #undef #undef" -@test replstr([1f10]) == "1-element Array{Float32,1}:\n 1.0f10" +@test replstr([1f10]) == "1-element Array{Float32,1}:\n 1.0e10" struct T5589 names::Vector{String} @@ -692,9 +694,9 @@ let io = IOBuffer() end # PR 17117 -# test show array +# test print_array let s = IOBuffer(Vector{UInt8}(), true, true) - Base.showarray(s, [1, 2, 3], false, header = false) + Base.print_array(s, [1, 2, 3]) @test String(resize!(s.data, s.size)) == " 1\n 2\n 3" end @@ -926,8 +928,7 @@ end @testset "Array printing with limited rows" begin arrstr = let buf = IOBuffer() function (A, rows) - Base.showarray(IOContext(buf, :displaysize => (rows, 80), :limit => true), - A, false, header=true) + Base._display(IOContext(buf, :displaysize => (rows, 80), :limit => true), A) String(take!(buf)) end end @@ -1025,3 +1026,19 @@ end show(IOContext(b, :module => @__MODULE__), TypeA) @test String(take!(b)) == "TypeA" end + +@testset "typeinfo" begin + @test replstr([[Int16(1)]]) == "1-element Array{Array{Int16,1},1}:\n [1]" + @test showstr([[Int16(1)]]) == "Array{Int16,1}[[1]]" + @test showstr(Set([[Int16(1)]])) == "Set(Array{Int16,1}[[1]])" + @test showstr([Float16(1)]) == "Float16[1.0]" + @test showstr([[Float16(1)]]) == "Array{Float16,1}[[1.0]]" + @test replstr(Real[Float16(1)]) == "1-element Array{Real,1}:\n Float16(1.0)" + @test replstr(Array{Real}[Real[1]]) == "1-element Array{Array{Real,N} where N,1}:\n [1]" + @testset "nested Any eltype" begin + x = Any[Any[Any[1]]] + # The element of x (i.e. x[1]) has an eltype which can't be deduced + # from eltype(x), so this must also be printed + @test replstr(x) == "1-element Array{Any,1}:\n Any[Any[1]]" + end +end