diff --git a/base/show.jl b/base/show.jl index 68279acb7756d..9d25110fb7fd5 100644 --- a/base/show.jl +++ b/base/show.jl @@ -1062,79 +1062,98 @@ function print_matrix_vdots(io::IO, end end +""" +`print_matrix(io, X)` composes an entire matrix, taking into account the screen size. +If X is too big, it will be nine-sliced with vertical, horizontal, or diagonal +ellipsis inserted as appropriate. +Optional parameters are screen size tuple sz such as (24,80), +string pre prior to the matrix (e.g. opening bracket), which will cause +a corresponding same-size indent on following rows, +string post on 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, sz::Tuple{Integer, Integer} = (s = tty_size(); (s[1]-4, s[2])), - pre::AbstractString = " ", - sep::AbstractString = " ", - post::AbstractString = "", + 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) - rows, cols = sz - cols -= length(pre) + length(post) - presp = repeat(" ", length(pre)) + screenheight, screenwidth = sz + screenwidth -= length(pre) + length(post) + presp = repeat(" ", length(pre)) # indent each row to match pre string postsp = "" @assert strwidth(hdots) == strwidth(ddots) - ss = length(sep) + sepsize = length(sep) m, n = size(X,1), size(X,2) - if m <= rows # rows fit - A = alignment(X,1:m,1:n,cols,cols,ss) - if n <= length(A) # rows and cols fit - for i = 1:m + # 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) + rowsA = m <= screenheight ? (1:m) : [1:halfheight; m-div(screenheight-1,2)+1:m] + # Similarly for columns, only necessary to get alignments for as many + # columns as could conceivably fit across the screen + maxpossiblecols = div(screenwidth, 1+sepsize) + colsA = n <= maxpossiblecols ? (1:n) : [1:maxpossiblecols; (n-maxpossiblecols+1):n] + A = alignment(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 == 1 ? pre : presp) - print_matrix_row(io, X,A,i,1:n,sep) + print_matrix_row(io, X,A,i,colsA,sep) print(io, i == m ? post : postsp) if i != m; println(io, ); end end - else # rows fit, cols don't - c = div(cols-length(hdots)+1,2)+1 - R = reverse(alignment(X,1:m,n:-1:1,c,c,ss)) - c = cols - sum(map(sum,R)) - (length(R)-1)*ss - length(hdots) - L = alignment(X,1:m,1:n,c,c,ss) - for i = 1:m + 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(X,rowsA,reverse(colsA),c,c,sepsize)) # alignments for right + c = screenwidth - sum(map(sum,Ralign)) - (length(Ralign)-1)*sepsize - length(hdots) + Lalign = alignment(X,rowsA,colsA,c,c,sepsize) # alignments for left of ellipsis + for i in rowsA print(io, i == 1 ? pre : presp) - print_matrix_row(io, X,L,i,1:length(L),sep) + print_matrix_row(io, X,Lalign,i,1:length(Lalign),sep) print(io, i % hmod == 1 ? hdots : repeat(" ", length(hdots))) - print_matrix_row(io, X,R,i,n-length(R)+1:n,sep) + print_matrix_row(io, X,Ralign,i,n-length(Ralign)+colsA,sep) print(io, i == m ? post : postsp) if i != m; println(io, ); end end end - else # rows don't fit - t = div(rows,2) - I = [1:t; m-div(rows-1,2)+1:m] - A = alignment(X,I,1:n,cols,cols,ss) - if n <= length(A) # rows don't fit, cols do - for i in I + 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 == 1 ? pre : presp) - print_matrix_row(io, X,A,i,1:n,sep) + print_matrix_row(io, X,A,i,colsA,sep) print(io, i == m ? post : postsp) - if i != I[end]; println(io, ); end - if i == t + if i != rowsA[end]; println(io, ); end + if i == halfheight print(io, i == 1 ? pre : presp) print_matrix_vdots(io, vdots,A,sep,vmod,1) println(io, i == m ? post : postsp) end end - else # neither rows nor cols fit - c = div(cols-length(hdots)+1,2)+1 - R = reverse(alignment(X,I,n:-1:1,c,c,ss)) - c = cols - sum(map(sum,R)) - (length(R)-1)*ss - length(hdots) - L = alignment(X,I,1:n,c,c,ss) - r = mod((length(R)-n+1),vmod) - for i in I + else # neither rows nor cols fit, so use all 3 kinds of dots + c = div(screenwidth-length(hdots)+1,2)+1 + Ralign = reverse(alignment(X,rowsA,reverse(colsA),c,c,sepsize)) + c = screenwidth - sum(map(sum,Ralign)) - (length(Ralign)-1)*sepsize - length(hdots) + Lalign = alignment(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 == 1 ? pre : presp) - print_matrix_row(io, X,L,i,1:length(L),sep) + print_matrix_row(io, X,Lalign,i,1:length(Lalign),sep) print(io, i % hmod == 1 ? hdots : repeat(" ", length(hdots))) - print_matrix_row(io, X,R,i,n-length(R)+1:n,sep) + print_matrix_row(io, X,Ralign,i,n-length(Ralign)+colsA,sep) print(io, i == m ? post : postsp) - if i != I[end]; println(io, ); end - if i == t + if i != rowsA[end]; println(io, ); end + if i == halfheight print(io, i == 1 ? pre : presp) - print_matrix_vdots(io, vdots,L,sep,vmod,1) + print_matrix_vdots(io, vdots,Lalign,sep,vmod,1) print(io, ddots) - print_matrix_vdots(io, vdots,R,sep,vmod,r) + print_matrix_vdots(io, vdots,Ralign,sep,vmod,r) println(io, i == m ? post : postsp) end end diff --git a/test/show.jl b/test/show.jl index 0fb185acbc365..846c57c5ce79b 100644 --- a/test/show.jl +++ b/test/show.jl @@ -302,3 +302,14 @@ function f13127() takebuf_string(buf) end @test f13127() == "f" + +# print_matrix should be able to handle small and large objects easily, test by +# calling writemime. This also indirectly tests print_matrix_row, which +# is used repeatedly by print_matrix. +# This fits on screen: +@test replstr(eye(10)) == "10x10 Array{Float64,2}:\n 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0\n 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0\n 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0\n 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0\n 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0\n 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0\n 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0\n 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0\n 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0\n 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0" +# an array too long vertically to fit on screen, and too long horizontally: +@test replstr(collect(1.:100.)) == "100-element Array{Float64,1}:\n 1.0\n 2.0\n 3.0\n 4.0\n 5.0\n 6.0\n 7.0\n 8.0\n 9.0\n 10.0\n ⋮ \n 92.0\n 93.0\n 94.0\n 95.0\n 96.0\n 97.0\n 98.0\n 99.0\n 100.0" +@test replstr(collect(1.:100.)') == "1x100 Array{Float64,2}:\n 1.0 2.0 3.0 4.0 5.0 6.0 7.0 … 95.0 96.0 97.0 98.0 99.0 100.0" +# too big in both directions to fit on screen: +@test replstr((1.:100.)*(1:100)') == "100x100 Array{Float64,2}:\n 1.0 2.0 3.0 4.0 5.0 6.0 … 97.0 98.0 99.0 100.0\n 2.0 4.0 6.0 8.0 10.0 12.0 194.0 196.0 198.0 200.0\n 3.0 6.0 9.0 12.0 15.0 18.0 291.0 294.0 297.0 300.0\n 4.0 8.0 12.0 16.0 20.0 24.0 388.0 392.0 396.0 400.0\n 5.0 10.0 15.0 20.0 25.0 30.0 485.0 490.0 495.0 500.0\n 6.0 12.0 18.0 24.0 30.0 36.0 … 582.0 588.0 594.0 600.0\n 7.0 14.0 21.0 28.0 35.0 42.0 679.0 686.0 693.0 700.0\n 8.0 16.0 24.0 32.0 40.0 48.0 776.0 784.0 792.0 800.0\n 9.0 18.0 27.0 36.0 45.0 54.0 873.0 882.0 891.0 900.0\n 10.0 20.0 30.0 40.0 50.0 60.0 970.0 980.0 990.0 1000.0\n ⋮ ⋮ ⋱ \n 92.0 184.0 276.0 368.0 460.0 552.0 8924.0 9016.0 9108.0 9200.0\n 93.0 186.0 279.0 372.0 465.0 558.0 9021.0 9114.0 9207.0 9300.0\n 94.0 188.0 282.0 376.0 470.0 564.0 9118.0 9212.0 9306.0 9400.0\n 95.0 190.0 285.0 380.0 475.0 570.0 9215.0 9310.0 9405.0 9500.0\n 96.0 192.0 288.0 384.0 480.0 576.0 … 9312.0 9408.0 9504.0 9600.0\n 97.0 194.0 291.0 388.0 485.0 582.0 9409.0 9506.0 9603.0 9700.0\n 98.0 196.0 294.0 392.0 490.0 588.0 9506.0 9604.0 9702.0 9800.0\n 99.0 198.0 297.0 396.0 495.0 594.0 9603.0 9702.0 9801.0 9900.0\n 100.0 200.0 300.0 400.0 500.0 600.0 9700.0 9800.0 9900.0 10000.0"