From 23f83d5f0f284d3526cbc947b6e330210fa571f4 Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Sat, 13 Jan 2018 23:25:49 +0100 Subject: [PATCH] move around and rename some show related stuff --- base/arrayshow.jl | 1 + base/{replutil.jl => errorshow.jl} | 158 ----------------------------- base/show.jl | 153 ++++++++++++++++++++++++++++ base/sysimg.jl | 4 +- test/choosetests.jl | 2 +- test/dict.jl | 43 +++++++- test/{replutil.jl => errorshow.jl} | 70 +------------ test/show.jl | 30 ++++++ 8 files changed, 232 insertions(+), 229 deletions(-) rename base/{replutil.jl => errorshow.jl} (80%) rename test/{replutil.jl => errorshow.jl} (90%) diff --git a/base/arrayshow.jl b/base/arrayshow.jl index e731588de8e85e..245a2056b62f4a 100644 --- a/base/arrayshow.jl +++ b/base/arrayshow.jl @@ -345,6 +345,7 @@ function _display(io::IO, X::AbstractArray) print_array(io, X) end +show(io::IO, ::MIME"text/plain", X::AbstractArray) = _display(io, X) ## printing with `show` diff --git a/base/replutil.jl b/base/errorshow.jl similarity index 80% rename from base/replutil.jl rename to base/errorshow.jl index a4e55aab981e7e..f89c35c2d00a0d 100644 --- a/base/replutil.jl +++ b/base/errorshow.jl @@ -1,163 +1,5 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license -# fallback text/plain representation of any type: -show(io::IO, ::MIME"text/plain", x) = show(io, x) - -# multiline show functions for types defined before multimedia.jl: -function show(io::IO, ::MIME"text/plain", iter::Union{KeySet,ValueIterator}) - print(io, summary(iter)) - isempty(iter) && return - print(io, ". ", isa(iter,KeySet) ? "Keys" : "Values", ":") - limit::Bool = get(io, :limit, false) - if limit - sz = displaysize(io) - rows, cols = sz[1] - 3, sz[2] - rows < 2 && (print(io, " …"); return) - cols < 4 && (cols = 4) - cols -= 2 # For prefix " " - rows -= 1 # For summary - else - rows = cols = typemax(Int) - end - - for (i, v) in enumerate(iter) - print(io, "\n ") - i == rows < length(iter) && (print(io, "⋮"); break) - - if limit - str = sprint(show, v, context=io, sizehint=0) - str = _truncate_at_width_or_chars(str, cols, "\r\n") - print(io, str) - else - show(io, v) - end - end -end - -function show(io::IO, ::MIME"text/plain", t::AbstractDict{K,V}) where {K,V} - # show more descriptively, with one line per key/value pair - recur_io = IOContext(io, :SHOWN_SET => t) - limit::Bool = get(io, :limit, false) - if !haskey(io, :compact) - recur_io = IOContext(recur_io, :compact => true) - end - - print(io, summary(t)) - isempty(t) && return - print(io, ":") - show_circular(io, t) && return - if limit - sz = displaysize(io) - rows, cols = sz[1] - 3, sz[2] - rows < 2 && (print(io, " …"); return) - cols < 12 && (cols = 12) # Minimum widths of 2 for key, 4 for value - cols -= 6 # Subtract the widths of prefix " " separator " => " - rows -= 1 # Subtract the summary - - # determine max key width to align the output, caching the strings - ks = Vector{AbstractString}(uninitialized, min(rows, length(t))) - vs = Vector{AbstractString}(uninitialized, min(rows, length(t))) - keylen = 0 - vallen = 0 - for (i, (k, v)) in enumerate(t) - i > rows && break - ks[i] = sprint(show, k, context=recur_io, sizehint=0) - vs[i] = sprint(show, v, context=recur_io, sizehint=0) - keylen = clamp(length(ks[i]), keylen, cols) - vallen = clamp(length(vs[i]), vallen, cols) - end - if keylen > max(div(cols, 2), cols - vallen) - keylen = max(cld(cols, 3), cols - vallen) - end - else - rows = cols = typemax(Int) - end - - for (i, (k, v)) in enumerate(t) - print(io, "\n ") - i == rows < length(t) && (print(io, rpad("⋮", keylen), " => ⋮"); break) - - if limit - key = rpad(_truncate_at_width_or_chars(ks[i], keylen, "\r\n"), keylen) - else - key = sprint(show, k, context=recur_io, sizehint=0) - end - print(recur_io, key) - print(io, " => ") - - if limit - val = _truncate_at_width_or_chars(vs[i], cols - keylen, "\r\n") - print(io, val) - else - show(recur_io, v) - end - end -end - -function show(io::IO, ::MIME"text/plain", f::Function) - ft = typeof(f) - mt = ft.name.mt - if isa(f, Core.IntrinsicFunction) - show(io, f) - id = Core.Intrinsics.bitcast(Int32, f) - print(io, " (intrinsic function #$id)") - elseif isa(f, Core.Builtin) - print(io, mt.name, " (built-in function)") - else - name = mt.name - isself = isdefined(ft.name.module, name) && - ft == typeof(getfield(ft.name.module, name)) - n = length(methods(f)) - m = n==1 ? "method" : "methods" - sname = string(name) - ns = (isself || '#' in sname) ? sname : string("(::", ft, ")") - what = startswith(ns, '@') ? "macro" : "generic function" - print(io, ns, " (", what, " with $n $m)") - end -end - -function show(io::IO, ::MIME"text/plain", r::LinSpace) - # show for linspace, e.g. - # linspace(1,3,7) - # 7-element LinSpace{Float64}: - # 1.0,1.33333,1.66667,2.0,2.33333,2.66667,3.0 - print(io, summary(r)) - if !isempty(r) - println(io, ":") - print_range(io, r) - end -end - -function show(io::IO, ::MIME"text/plain", t::Task) - show(io, t) - if t.state == :failed - println(io) - showerror(io, CapturedException(t.result, t.backtrace)) - end -end - -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 - -function show(io::IO, ::MIME"text/plain", opt::JLOptions) - println(io, "JLOptions(") - fields = fieldnames(JLOptions) - nfields = length(fields) - for (i, f) in enumerate(fields) - v = getfield(opt, i) - if isa(v, Ptr{UInt8}) - v = (v != C_NULL) ? unsafe_string(v) : "" - elseif isa(v, Ptr{Ptr{UInt8}}) - v = unsafe_load_commands(v) - end - println(io, " ", f, " = ", repr(v), i < nfields ? "," : "") - end - print(io, ")") -end - - -# showing exception objects as descriptive error messages - """ showerror(io, e) diff --git a/base/show.jl b/base/show.jl index 95e4aebf7e160f..52688f814fa281 100644 --- a/base/show.jl +++ b/base/show.jl @@ -1,5 +1,158 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license +# first a few multiline show functions for types defined before the MIME type: + +show(io::IO, ::MIME"text/plain", r::AbstractRange) = show(io, r) # always use the compact form for printing ranges + +function show(io::IO, ::MIME"text/plain", r::LinSpace) + # show for linspace, e.g. + # linspace(1,3,7) + # 7-element LinSpace{Float64}: + # 1.0,1.33333,1.66667,2.0,2.33333,2.66667,3.0 + print(io, summary(r)) + if !isempty(r) + println(io, ":") + print_range(io, r) + end +end + +function show(io::IO, ::MIME"text/plain", f::Function) + ft = typeof(f) + mt = ft.name.mt + if isa(f, Core.IntrinsicFunction) + show(io, f) + id = Core.Intrinsics.bitcast(Int32, f) + print(io, " (intrinsic function #$id)") + elseif isa(f, Core.Builtin) + print(io, mt.name, " (built-in function)") + else + name = mt.name + isself = isdefined(ft.name.module, name) && + ft == typeof(getfield(ft.name.module, name)) + n = length(methods(f)) + m = n==1 ? "method" : "methods" + sname = string(name) + ns = (isself || '#' in sname) ? sname : string("(::", ft, ")") + what = startswith(ns, '@') ? "macro" : "generic function" + print(io, ns, " (", what, " with $n $m)") + end +end + +function show(io::IO, ::MIME"text/plain", iter::Union{KeySet,ValueIterator}) + print(io, summary(iter)) + isempty(iter) && return + print(io, ". ", isa(iter,KeySet) ? "Keys" : "Values", ":") + limit::Bool = get(io, :limit, false) + if limit + sz = displaysize(io) + rows, cols = sz[1] - 3, sz[2] + rows < 2 && (print(io, " …"); return) + cols < 4 && (cols = 4) + cols -= 2 # For prefix " " + rows -= 1 # For summary + else + rows = cols = typemax(Int) + end + + for (i, v) in enumerate(iter) + print(io, "\n ") + i == rows < length(iter) && (print(io, "⋮"); break) + + if limit + str = sprint(show, v, context=io, sizehint=0) + str = _truncate_at_width_or_chars(str, cols, "\r\n") + print(io, str) + else + show(io, v) + end + end +end + +function show(io::IO, ::MIME"text/plain", t::AbstractDict{K,V}) where {K,V} + # show more descriptively, with one line per key/value pair + recur_io = IOContext(io, :SHOWN_SET => t) + limit::Bool = get(io, :limit, false) + if !haskey(io, :compact) + recur_io = IOContext(recur_io, :compact => true) + end + + print(io, summary(t)) + isempty(t) && return + print(io, ":") + show_circular(io, t) && return + if limit + sz = displaysize(io) + rows, cols = sz[1] - 3, sz[2] + rows < 2 && (print(io, " …"); return) + cols < 12 && (cols = 12) # Minimum widths of 2 for key, 4 for value + cols -= 6 # Subtract the widths of prefix " " separator " => " + rows -= 1 # Subtract the summary + + # determine max key width to align the output, caching the strings + ks = Vector{AbstractString}(uninitialized, min(rows, length(t))) + vs = Vector{AbstractString}(uninitialized, min(rows, length(t))) + keylen = 0 + vallen = 0 + for (i, (k, v)) in enumerate(t) + i > rows && break + ks[i] = sprint(show, k, context=recur_io, sizehint=0) + vs[i] = sprint(show, v, context=recur_io, sizehint=0) + keylen = clamp(length(ks[i]), keylen, cols) + vallen = clamp(length(vs[i]), vallen, cols) + end + if keylen > max(div(cols, 2), cols - vallen) + keylen = max(cld(cols, 3), cols - vallen) + end + else + rows = cols = typemax(Int) + end + + for (i, (k, v)) in enumerate(t) + print(io, "\n ") + i == rows < length(t) && (print(io, rpad("⋮", keylen), " => ⋮"); break) + + if limit + key = rpad(_truncate_at_width_or_chars(ks[i], keylen, "\r\n"), keylen) + else + key = sprint(show, k, context=recur_io, sizehint=0) + end + print(recur_io, key) + print(io, " => ") + + if limit + val = _truncate_at_width_or_chars(vs[i], cols - keylen, "\r\n") + print(io, val) + else + show(recur_io, v) + end + end +end + +function show(io::IO, ::MIME"text/plain", opt::JLOptions) + println(io, "JLOptions(") + fields = fieldnames(JLOptions) + nfields = length(fields) + for (i, f) in enumerate(fields) + v = getfield(opt, i) + if isa(v, Ptr{UInt8}) + v = (v != C_NULL) ? unsafe_string(v) : "" + elseif isa(v, Ptr{Ptr{UInt8}}) + v = unsafe_load_commands(v) + end + println(io, " ", f, " = ", repr(v), i < nfields ? "," : "") + end + print(io, ")") +end + +function show(io::IO, ::MIME"text/plain", t::Task) + show(io, t) + if t.state == :failed + println(io) + showerror(io, CapturedException(t.result, t.backtrace)) + end +end + + print(io::IO, s::Symbol) = (write(io,s); nothing) """ diff --git a/base/sysimg.jl b/base/sysimg.jl index 0ea031a6bff563..84b8bc42dfbe14 100644 --- a/base/sysimg.jl +++ b/base/sysimg.jl @@ -182,6 +182,8 @@ struct MIME{mime} end macro MIME_str(s) :(MIME{$(Expr(:quote, Symbol(s)))}) end +# fallback text/plain representation of any type: +show(io::IO, ::MIME"text/plain", x) = show(io, x) # SIMD loops include("simdloop.jl") @@ -416,7 +418,7 @@ include("channels.jl") include("deepcopy.jl") include("interactiveutil.jl") include("summarysize.jl") -include("replutil.jl") +include("errorshow.jl") include("i18n.jl") using .I18n diff --git a/test/choosetests.jl b/test/choosetests.jl index a367f7effc1c50..0dae90ff4e05a4 100644 --- a/test/choosetests.jl +++ b/test/choosetests.jl @@ -47,7 +47,7 @@ function choosetests(choices = []) "floatapprox", "stdlib", "reflection", "regex", "float16", "combinatorics", "sysinfo", "env", "rounding", "ranges", "mod2pi", "euler", "show", - "replutil", "sets", "goto", "llvmcall", "llvmcall2", "grisu", + "errorshow", "sets", "goto", "llvmcall", "llvmcall2", "grisu", "some", "meta", "stacktraces", "libgit2", "docs", "markdown", "misc", "threads", "enums", "cmdlineargs", "i18n", "int", diff --git a/test/dict.jl b/test/dict.jl index e1be57a292953a..dd1aa158abbbad 100644 --- a/test/dict.jl +++ b/test/dict.jl @@ -864,4 +864,45 @@ end @test findfirst(equalto(1), Dict(:a=>1, :b=>1, :c=>3)) in (:a, :b) @test findfirst(equalto(1), Dict()) === nothing @test findfirst(equalto(1), Dict(:a=>2, :b=>3)) === nothing -end \ No newline at end of file +end + +@testset "Dict printing with limited rows" begin + local buf + buf = IOBuffer() + io = IOContext(buf, :displaysize => (4, 80), :limit => true) + d = Base.ImmutableDict(1=>2) + show(io, MIME"text/plain"(), d) + @test String(take!(buf)) == "Base.ImmutableDict{$Int,$Int} with 1 entry: …" + show(io, MIME"text/plain"(), keys(d)) + @test String(take!(buf)) == + "Base.KeySet for a Base.ImmutableDict{$Int,$Int} with 1 entry. Keys: …" + + io = IOContext(io, :displaysize => (5, 80)) + show(io, MIME"text/plain"(), d) + @test String(take!(buf)) == "Base.ImmutableDict{$Int,$Int} with 1 entry:\n 1 => 2" + show(io, MIME"text/plain"(), keys(d)) + @test String(take!(buf)) == + "Base.KeySet for a Base.ImmutableDict{$Int,$Int} with 1 entry. Keys:\n 1" + d = Base.ImmutableDict(d, 3=>4) + show(io, MIME"text/plain"(), d) + @test String(take!(buf)) == "Base.ImmutableDict{$Int,$Int} with 2 entries:\n ⋮ => ⋮" + show(io, MIME"text/plain"(), keys(d)) + @test String(take!(buf)) == + "Base.KeySet for a Base.ImmutableDict{$Int,$Int} with 2 entries. Keys:\n ⋮" + + io = IOContext(io, :displaysize => (6, 80)) + show(io, MIME"text/plain"(), d) + @test String(take!(buf)) == + "Base.ImmutableDict{$Int,$Int} with 2 entries:\n 3 => 4\n 1 => 2" + show(io, MIME"text/plain"(), keys(d)) + @test String(take!(buf)) == + "Base.KeySet for a Base.ImmutableDict{$Int,$Int} with 2 entries. Keys:\n 3\n 1" + d = Base.ImmutableDict(d, 5=>6) + show(io, MIME"text/plain"(), d) + @test String(take!(buf)) == + "Base.ImmutableDict{$Int,$Int} with 3 entries:\n 5 => 6\n ⋮ => ⋮" + show(io, MIME"text/plain"(), keys(d)) + @test String(take!(buf)) == + "Base.KeySet for a Base.ImmutableDict{$Int,$Int} with 3 entries. Keys:\n 5\n ⋮" +end + diff --git a/test/replutil.jl b/test/errorshow.jl similarity index 90% rename from test/replutil.jl rename to test/errorshow.jl index 91abb3c1d61dc0..349e46c1317d99 100644 --- a/test/replutil.jl +++ b/test/errorshow.jl @@ -409,21 +409,6 @@ withenv("JULIA_EDITOR" => nothing, "VISUAL" => nothing, "EDITOR" => nothing) do @test Base.editor() == ["/Applications/Sublime Text.app/Contents/SharedSupport/bin/subl", "-w"] end -# Issue #14684: `display` should print associative types in full. -let d = Dict(1 => 2, 3 => 45), - buf = IOBuffer(), - td = TextDisplay(buf) - - display(td, d) - result = String(take!(td.io)) - @test contains(result, summary(d)) - - # Is every pair in the string? - for el in d - @test contains(result, string(el)) - end -end - # Issue #20108 let err, buf = IOBuffer() try Array() catch err end @@ -432,12 +417,6 @@ let err, buf = IOBuffer() @test contains(String(take!(buf)), "Closest candidates are:") end -# Issue 20111 -let K20111(x) = y -> x, buf = IOBuffer() - show(buf, methods(K20111(1))) - @test contains(String(take!(buf)), " 1 method for generic function") -end - # @macroexpand tests macro seven_dollar(ex) # simonbyrne example 18240 @@ -506,6 +485,7 @@ foo_9965(x::Int) = 2x end # Issue #20556 +import REPL module EnclosingModule abstract type AbstractTypeNoConstructors end end @@ -526,7 +506,7 @@ let @test !contains(sprint(showerror, method_error), "where T at sysimg.jl") # Test that tab-completion will not show the 'default' sysimg.jl method. - for method_string in Base.REPLCompletions.complete_methods(:(EnclosingModule.AbstractTypeNoConstructors())) + for method_string in REPL.REPLCompletions.complete_methods(:(EnclosingModule.AbstractTypeNoConstructors())) @test !startswith(method_string, "(::Type{T})(arg) where T in Base at sysimg.jl") end end @@ -573,49 +553,3 @@ end end end -# issue #22798 -@generated f22798(x::Integer, y) = :x -let buf = IOBuffer() - show(buf, methods(f22798)) - @test contains(String(take!(buf)), "f22798(x::Integer, y)") -end - -@testset "Dict printing with limited rows" begin - local buf - buf = IOBuffer() - io = IOContext(buf, :displaysize => (4, 80), :limit => true) - d = Base.ImmutableDict(1=>2) - show(io, MIME"text/plain"(), d) - @test String(take!(buf)) == "Base.ImmutableDict{$Int,$Int} with 1 entry: …" - show(io, MIME"text/plain"(), keys(d)) - @test String(take!(buf)) == - "Base.KeySet for a Base.ImmutableDict{$Int,$Int} with 1 entry. Keys: …" - - io = IOContext(io, :displaysize => (5, 80)) - show(io, MIME"text/plain"(), d) - @test String(take!(buf)) == "Base.ImmutableDict{$Int,$Int} with 1 entry:\n 1 => 2" - show(io, MIME"text/plain"(), keys(d)) - @test String(take!(buf)) == - "Base.KeySet for a Base.ImmutableDict{$Int,$Int} with 1 entry. Keys:\n 1" - d = Base.ImmutableDict(d, 3=>4) - show(io, MIME"text/plain"(), d) - @test String(take!(buf)) == "Base.ImmutableDict{$Int,$Int} with 2 entries:\n ⋮ => ⋮" - show(io, MIME"text/plain"(), keys(d)) - @test String(take!(buf)) == - "Base.KeySet for a Base.ImmutableDict{$Int,$Int} with 2 entries. Keys:\n ⋮" - - io = IOContext(io, :displaysize => (6, 80)) - show(io, MIME"text/plain"(), d) - @test String(take!(buf)) == - "Base.ImmutableDict{$Int,$Int} with 2 entries:\n 3 => 4\n 1 => 2" - show(io, MIME"text/plain"(), keys(d)) - @test String(take!(buf)) == - "Base.KeySet for a Base.ImmutableDict{$Int,$Int} with 2 entries. Keys:\n 3\n 1" - d = Base.ImmutableDict(d, 5=>6) - show(io, MIME"text/plain"(), d) - @test String(take!(buf)) == - "Base.ImmutableDict{$Int,$Int} with 3 entries:\n 5 => 6\n ⋮ => ⋮" - show(io, MIME"text/plain"(), keys(d)) - @test String(take!(buf)) == - "Base.KeySet for a Base.ImmutableDict{$Int,$Int} with 3 entries. Keys:\n 5\n ⋮" -end diff --git a/test/show.jl b/test/show.jl index a604a29a23eb52..1b155865bc622e 100644 --- a/test/show.jl +++ b/test/show.jl @@ -1097,3 +1097,33 @@ end A = [0.0, 1.0] @test replstr(view(A, [1], :)) == "1×1 view(::Array{Float64,2}, [1], :) with eltype Float64:\n 0.0" end + +@testset "#14684: `display` should print associative types in full" begin + d = Dict(1 => 2, 3 => 45) + buf = IOBuffer() + td = TextDisplay(buf) + + display(td, d) + result = String(take!(td.io)) + @test contains(result, summary(d)) + + # Is every pair in the string? + for el in d + @test contains(result, string(el)) + end +end + +@testset "#20111 show for function" begin + K20111(x) = y -> x + buf = IOBuffer() + show(buf, methods(K20111(1))) + @test contains(String(take!(buf)), " 1 method for generic function") +end + +@generated f22798(x::Integer, y) = :x +@testset "#22798" begin + buf = IOBuffer() + show(buf, methods(f22798)) + @test contains(String(take!(buf)), "f22798(x::Integer, y)") +end +