From cf862eb7c79bec4bee9b16878ebf6ac1dd7a7500 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Thu, 29 Oct 2015 14:28:16 -0400 Subject: [PATCH 1/6] refactor AbstractPipe in terms of pipe_reader/pipe_writer instead of .out/.in --- base/process.jl | 4 ++++ base/stream.jl | 59 +++++++++++++++++++++++++++---------------------- 2 files changed, 36 insertions(+), 27 deletions(-) diff --git a/base/process.jl b/base/process.jl index 3e36e07607fc2..cd1858995cd8c 100644 --- a/base/process.jl +++ b/base/process.jl @@ -303,6 +303,8 @@ type Process <: AbstractPipe this end end +pipe_reader(p::Process) = p.out +pipe_writer(p::Process) = p.in immutable ProcessChain <: AbstractPipe processes::Vector{Process} @@ -311,6 +313,8 @@ immutable ProcessChain <: AbstractPipe err::Redirectable ProcessChain(stdios::StdIOSet) = new(Process[], stdios[1], stdios[2], stdios[3]) end +pipe_reader(p::ProcessChain) = p.out +pipe_writer(p::ProcessChain) = p.in function _jl_spawn(cmd, argv, loop::Ptr{Void}, pp::Process, in, out, err) diff --git a/base/stream.jl b/base/stream.jl index e13eb3f979039..846774f559423 100644 --- a/base/stream.jl +++ b/base/stream.jl @@ -529,14 +529,18 @@ end # (composed of two half-pipes: .in and .out) ########################################## +# allows sharing implementation of wrappers around other IO objects abstract AbstractPipe <: IO -# allows sharing implementation with Process and ProcessChain +function pipe_reader end +function pipe_writer end type Pipe <: AbstractPipe in::PipeEndpoint # writable out::PipeEndpoint # readable end Pipe() = Pipe(PipeEndpoint(), PipeEndpoint()) +pipe_reader(p::Pipe) = p.out +pipe_writer(p::Pipe) = p.in function link_pipe(pipe::Pipe; julia_only_read = false, @@ -544,37 +548,38 @@ function link_pipe(pipe::Pipe; link_pipe(pipe.out, julia_only_read, pipe.in, julia_only_write); end -show(io::IO,stream::Pipe) = print(io, +show(io::IO, stream::Pipe) = print(io, "Pipe(", uv_status_string(stream.in), " => ", uv_status_string(stream.out), ", ", nb_available(stream), " bytes waiting)") -write(io::AbstractPipe, byte::UInt8) = write(io.in, byte) -write(io::AbstractPipe, bytes::Vector{UInt8}) = write(io.in, bytes) -write{T<:AbstractPipe}(io::T, args...) = write(io.in, args...) -write{S<:AbstractPipe}(io::S, a::Array) = write(io.in, a) -buffer_or_write(io::AbstractPipe, p::Ptr, n::Integer) = buffer_or_write(io.in, p, n) -buffer_writes(io::AbstractPipe, args...) = buffer_writes(io.in, args...) -flush(io::AbstractPipe) = flush(io.in) - -read(io::AbstractPipe, byte::Type{UInt8}) = read(io.out, byte) -read!(io::AbstractPipe, bytes::Vector{UInt8}) = read!(io.out, bytes) -read{T<:AbstractPipe}(io::T, args...) = read(io.out, args...) -read!{T<:AbstractPipe}(io::T, args...) = read!(io.out, args...) -readuntil{T<:AbstractPipe}(io::T, args...) = readuntil(io.out, args...) -readbytes(io::AbstractPipe) = readbytes(io.out) -readavailable(io::AbstractPipe) = readavailable(io.out) - -isreadable(io::AbstractPipe) = isreadable(io.out) -iswritable(io::AbstractPipe) = iswritable(io.in) -isopen(io::AbstractPipe) = isopen(io.in) || isopen(io.out) -close(io::AbstractPipe) = (close(io.in); close(io.out)) -wait_readnb(io::AbstractPipe, nb::Int) = wait_readnb(io.out, nb) -wait_readbyte(io::AbstractPipe, byte::UInt8) = wait_readbyte(io.out, byte) -wait_close(io::AbstractPipe) = (wait_close(io.in); wait_close(io.out)) -nb_available(io::AbstractPipe) = nb_available(io.out) -eof(io::AbstractPipe) = eof(io.out) +write(io::AbstractPipe, byte::UInt8) = write(pipe_writer(io), byte) +write(io::AbstractPipe, bytes::Vector{UInt8}) = write(pipe_writer(io), bytes) +write{T<:AbstractPipe}(io::T, args...) = write(pipe_writer(io), args...) +write{S<:AbstractPipe}(io::S, a::Array) = write(pipe_writer(io), a) +buffer_or_write(io::AbstractPipe, p::Ptr, n::Integer) = buffer_or_write(pipe_writer(io), p, n) +buffer_writes(io::AbstractPipe, args...) = buffer_writes(pipe_writer(io), args...) +flush(io::AbstractPipe) = flush(pipe_writer(io)) + +read(io::AbstractPipe, byte::Type{UInt8}) = read(pipe_reader(io), byte) +read!(io::AbstractPipe, bytes::Vector{UInt8}) = read!(pipe_reader(io), bytes) +read{T<:AbstractPipe}(io::T, args...) = read(pipe_reader(io), args...) +read!{T<:AbstractPipe}(io::T, args...) = read!(pipe_reader(io), args...) +readuntil{T<:AbstractPipe}(io::T, args...) = readuntil(pipe_reader(io), args...) +readbytes(io::AbstractPipe) = readbytes(pipe_reader(io)) +readavailable(io::AbstractPipe) = readavailable(pipe_reader(io)) + +isreadable(io::AbstractPipe) = isreadable(pipe_reader(io)) +iswritable(io::AbstractPipe) = iswritable(pipe_writer(io)) +isopen(io::AbstractPipe) = isopen(pipe_writer(io)) || isopen(pipe_reader(io)) +close(io::AbstractPipe) = (close(pipe_writer(io)); close(pipe_reader(io))) +wait_readnb(io::AbstractPipe, nb::Int) = wait_readnb(pipe_reader(io), nb) +wait_readbyte(io::AbstractPipe, byte::UInt8) = wait_readbyte(pipe_reader(io), byte) +wait_close(io::AbstractPipe) = (wait_close(pipe_writer(io)); wait_close(pipe_reader(io))) +nb_available(io::AbstractPipe) = nb_available(pipe_reader(io)) +eof(io::AbstractPipe) = eof(pipe_reader(io)) +reseteof(io::AbstractPipe) = reseteof(pipe_reader(io)) ########################################## # Async Worker From 1fe13919da45c5b1718635fb69cfaca19905b46c Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Thu, 24 Dec 2015 02:25:47 -0500 Subject: [PATCH 2/6] implement an ImmutableDict type this is designed to be used as the basis for the IOContext type also fix a bug in testing equality of a self-referential dict (with added test) --- base/dict.jl | 111 ++++++++++++++++++++++++++++++++++++++++++++++++--- test/dict.jl | 73 ++++++++++++++++++++++++++++++++- 2 files changed, 177 insertions(+), 7 deletions(-) diff --git a/base/dict.jl b/base/dict.jl index e7cee54e7210e..a6600bf8b382a 100644 --- a/base/dict.jl +++ b/base/dict.jl @@ -8,9 +8,20 @@ const secret_table_token = :__c782dbf1cf4d6a2e5e3865d7e95634f2e09b5902__ haskey(d::Associative, k) = in(k,keys(d)) -function in(p::Pair, a::Associative) +function in(p::Pair, a::Associative, valcmp=(==)) v = get(a,p[1],secret_table_token) - !is(v, secret_table_token) && (v == p[2]) + if !is(v, secret_table_token) + if valcmp === is + is(v, p[2]) && return true + elseif valcmp === (==) + ==(v, p[2]) && return true + elseif valcmp === isequal + isequal(v, p[2]) && return true + else + valcmp(v, p[2]) && return true + end + end + return false end function in(p, a::Associative) @@ -245,12 +256,13 @@ filter(f, d::Associative) = filter!(f,copy(d)) eltype{K,V}(::Type{Associative{K,V}}) = Pair{K,V} function isequal(l::Associative, r::Associative) + l === r && return true if isa(l,ObjectIdDict) != isa(r,ObjectIdDict) return false end if length(l) != length(r) return false end - for (key, value) in l - if !isequal(value, get(r, key, secret_table_token)) + for pair in l + if !in(pair, r, isequal) return false end end @@ -258,12 +270,13 @@ function isequal(l::Associative, r::Associative) end function ==(l::Associative, r::Associative) + l === r && return true if isa(l,ObjectIdDict) != isa(r,ObjectIdDict) return false end if length(l) != length(r) return false end - for (key, value) in l - if value != get(r, key, secret_table_token) + for pair in l + if !in(pair, r, ==) return false end end @@ -855,3 +868,89 @@ function next{K,V}(t::WeakKeyDict{K,V}, i) (Pair{K,V}(kv[1].value::K,kv[2]), i) end length(t::WeakKeyDict) = length(t.ht) + + +immutable ImmutableDict{K, V} <: Associative{K,V} + parent::ImmutableDict{K, V} + key::K + value::V + ImmutableDict() = new() # represents an empty dictionary + ImmutableDict(key, value) = (empty = new(); new(empty, key, value)) + ImmutableDict(parent::ImmutableDict, key, value) = new(parent, key, value) +end + +""" + ImmutableDict + +ImmutableDict is a Dictionary implemented as an immutable linked list, +which is optimal for small dictionaries that are constructed over many individual insertions +Note that it is not possible to remove a value, although it can be partially overridden and hidden +by inserting a new value with the same key + + ImmutableDict(KV::Pair) + +Create a new entry in the Immutable Dictionary for the key => value pair + + - use `(key => value) in dict` to see if this particular combination is in the properties set + - use `get(dict, key, default)` to retrieve the most recent value for a particular key + +""" +ImmutableDict +ImmutableDict{K,V}(KV::Pair{K,V}) = ImmutableDict{K,V}(KV[1], KV[2]) +ImmutableDict{K,V}(t::ImmutableDict{K,V}, KV::Pair) = ImmutableDict{K,V}(t, KV[1], KV[2]) + +function in(key_value::Pair, dict::ImmutableDict, valcmp=(==)) + key, value = key_value + while isdefined(dict, :parent) + if dict.key == key + if valcmp === is + is(value, dict.value) && return true + elseif valcmp === (==) + ==(value, dict.value) && return true + elseif valcmp === isequal + isequal(value, dict.value) && return true + else + valcmp(value, dict.value) && return true + end + end + dict = dict.parent + end + return false +end + +function haskey(dict::ImmutableDict, key) + while isdefined(dict, :parent) + dict.key == key && return true + dict = dict.parent + end + return false +end + +function getindex(dict::ImmutableDict, key) + while isdefined(dict, :parent) + dict.key == key && return dict.value + dict = dict.parent + end + throw(KeyError(key)) +end +function get(dict::ImmutableDict, key, default) + while isdefined(dict, :parent) + dict.key == key && return dict.value + dict = dict.parent + end + return default +end + +# this actually defines reverse iteration (e.g. it should not be used for merge/copy/filter type operations) +start(t::ImmutableDict) = t +next{K,V}(::ImmutableDict{K,V}, t) = (Pair{K,V}(t.key, t.value), t.parent) +done(::ImmutableDict, t) = !isdefined(t, :parent) +length(t::ImmutableDict) = count(x->1, t) +isempty(t::ImmutableDict) = done(t, start(t)) +copy(t::ImmutableDict) = t +function similar(t::ImmutableDict) + while isdefined(t, :parent) + t = t.parent + end + return t +end diff --git a/test/dict.jl b/test/dict.jl index 6aa61740d8560..59fa26d4835c9 100644 --- a/test/dict.jl +++ b/test/dict.jl @@ -329,17 +329,19 @@ let end # issue #10647 +type T10647{T}; x::T; end let a = ObjectIdDict() a[1] = a a[a] = 2 - type T10647{T}; x::T; end a[3] = T10647(a) + @test a == a show(IOBuffer(), a) Base.showdict(IOBuffer(), a) Base.showdict(IOBuffer(), a; limit=true) end + # Issue #7944 let d = Dict{Int,Int}() get!(d, 0) do @@ -358,3 +360,72 @@ d = Dict('a'=>1, 'b'=>1, 'c'=> 3) @test_throws ArgumentError Dict(0) @test_throws ArgumentError Dict([1]) @test_throws ArgumentError Dict([(1,2),0]) + +# ImmutableDict +import Base.ImmutableDict +let d = ImmutableDict{UTF8String, UTF8String}(), + k1 = UTF8String("key1"), + k2 = UTF8String("key2"), + v1 = UTF8String("value1"), + v2 = UTF8String("value2"), + d1 = ImmutableDict(d, k1 => v1), + d2 = ImmutableDict(d1, k2 => v2), + d3 = ImmutableDict(d2, k1 => v2), + d4 = ImmutableDict(d3, k2 => v1), + dnan = ImmutableDict{UTF8String, Float64}(k2, NaN), + dnum = ImmutableDict(dnan, k2 => 1) + + @test isempty(collect(d)) + @test !isempty(collect(d1)) + @test isempty(d) + @test !isempty(d1) + @test length(d) == 0 + @test length(d1) == 1 + @test length(d2) == 2 + @test length(d3) == 3 + @test length(d4) == 4 + @test !(k1 in keys(d)) + @test k1 in keys(d1) + @test k1 in keys(d2) + @test k1 in keys(d3) + @test k1 in keys(d4) + + @test !haskey(d, k1) + @test haskey(d1, k1) + @test haskey(d2, k1) + @test haskey(d3, k1) + @test haskey(d4, k1) + @test !(k2 in keys(d1)) + @test k2 in keys(d2) + @test !(k1 in values(d4)) + @test v1 in values(d4) + @test collect(d1) == [Pair(k1, v1)] + @test collect(d4) == reverse([Pair(k1, v1), Pair(k2, v2), Pair(k1, v2), Pair(k2, v1)]) + @test d1 == ImmutableDict(d, k1 => v1) + @test !((k1 => v2) in d2) + @test (k1 => v2) in d3 + @test (k1 => v1) in d4 + @test (k1 => v2) in d4 + @test !in(k2 => "value2", d4, is) + @test in(k2 => v2, d4, is) + @test in(k2 => NaN, dnan, isequal) + @test in(k2 => NaN, dnan, is) + @test !in(k2 => NaN, dnan, ==) + @test !in(k2 => 1, dnum, is) + @test in(k2 => 1.0, dnum, is) + @test !in(k2 => 1, dnum, <) + @test in(k2 => 0, dnum, <) + @test get(d1, "key1", :default) === v1 + @test get(d4, "key1", :default) === v2 + @test get(d4, "foo", :default) === :default + @test get(d, k1, :default) === :default + @test d1["key1"] === v1 + @test d4["key1"] === v2 + @test copy(d4) === d4 + @test copy(d) === d + @test similar(d3) === d + @test similar(d) === d + + @test_throws KeyError d[k1] + @test_throws KeyError d1["key2"] +end From 4706184a424eda72aa802f465c7f45c5545143e0 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Thu, 29 Oct 2015 16:38:43 -0400 Subject: [PATCH 3/6] reimplement show context global/tls variables as local state contained in a new IOContext lightweight wrapper type --- base/Enums.jl | 7 +- base/complex.jl | 11 +- base/dict.jl | 139 +++++++-------- base/docs/helpdb/Base.jl | 5 +- base/grisu.jl | 2 +- base/interactiveutil.jl | 28 ++- base/io.jl | 33 ++++ base/iostream.jl | 9 - base/irrationals.jl | 6 +- base/methodshow.jl | 2 +- base/mpfr.jl | 3 +- base/precompile.jl | 4 +- base/range.jl | 6 +- base/replutil.jl | 14 +- base/show.jl | 241 +++++++++++++++----------- base/sparse/sparsematrix.jl | 6 +- base/sparse/sparsevector.jl | 8 +- base/stream.jl | 32 ---- base/strings/io.jl | 29 +++- base/unicode/utf8proc.jl | 2 +- doc/manual/networking-and-streams.rst | 5 + doc/stdlib/io-network.rst | 23 +++ test/dict.jl | 11 +- 23 files changed, 344 insertions(+), 282 deletions(-) diff --git a/base/Enums.jl b/base/Enums.jl index b86150985c74e..9937565a114bf 100644 --- a/base/Enums.jl +++ b/base/Enums.jl @@ -93,9 +93,12 @@ macro enum(T,syms...) end end function Base.show(io::IO,x::$(esc(typename))) - print(io, x, "::", $(esc(typename)), " = ", Int(x)) + if Base.limit_output(io) + print(io, x) + else + print(io, x, "::", $(esc(typename)), " = ", Int(x)) + end end - Base.showcompact(io::IO,x::$(esc(typename))) = print(io, x) function Base.writemime(io::IO,::MIME"text/plain",::Type{$(esc(typename))}) print(io, "Enum ", $(esc(typename)), ":") for (sym, i) in $vals diff --git a/base/complex.jl b/base/complex.jl index 8d98ae0c7aabf..b1b4e039876bd 100644 --- a/base/complex.jl +++ b/base/complex.jl @@ -59,25 +59,24 @@ complex(x::Real, y::Real) = Complex(x, y) complex(x::Real) = Complex(x) complex(z::Complex) = z -function complex_show(io::IO, z::Complex, compact::Bool) +function show(io::IO, z::Complex) r, i = reim(z) - compact ? showcompact(io,r) : show(io,r) + compact = limit_output(io) + showcompact_lim(io, r) if signbit(i) && !isnan(i) i = -i print(io, compact ? "-" : " - ") else print(io, compact ? "+" : " + ") end - compact ? showcompact(io, i) : show(io, i) + showcompact_lim(io, i) if !(isa(i,Integer) && !isa(i,Bool) || isa(i,AbstractFloat) && isfinite(i)) print(io, "*") end print(io, "im") end -complex_show(io::IO, z::Complex{Bool}, compact::Bool) = +show(io::IO, z::Complex{Bool}) = print(io, z == im ? "im" : "Complex($(z.re),$(z.im))") -show(io::IO, z::Complex) = complex_show(io, z, false) -showcompact(io::IO, z::Complex) = complex_show(io, z, true) function read{T<:Real}(s::IO, ::Type{Complex{T}}) r = read(s,T) diff --git a/base/dict.jl b/base/dict.jl index a6600bf8b382a..09c0bb96a6b5c 100644 --- a/base/dict.jl +++ b/base/dict.jl @@ -61,87 +61,79 @@ function _truncate_at_width_or_chars(str, width, chars="", truncmark="…") end showdict(t::Associative; kw...) = showdict(STDOUT, t; kw...) -function showdict{K,V}(io::IO, t::Associative{K,V}; limit::Bool = false, compact = false, +function showdict{K,V}(io::IO, t::Associative{K,V}; compact = false, sz=(s = tty_size(); (s[1]-3, s[2]))) - shown_set = get(task_local_storage(), :SHOWNSET, nothing) - if shown_set === nothing - shown_set = ObjectIdDict() - task_local_storage(:SHOWNSET, shown_set) - end - t in keys(shown_set) && (print(io, "#= circular reference =#"); return) - - try - shown_set[t] = true - if compact - # show in a Julia-syntax-like form: Dict(k=>v, ...) - if isempty(t) - print(io, typeof(t), "()") + (:SHOWN_SET => t) in io && (print(io, "#= circular reference =#"); return) + + recur_io = IOContext(io, :SHOWN_SET => t) + limit::Bool = limit_output(io) + if compact + # show in a Julia-syntax-like form: Dict(k=>v, ...) + if isempty(t) + print(io, typeof(t), "()") + else + if isleaftype(K) && isleaftype(V) + print(io, typeof(t).name) else - if isleaftype(K) && isleaftype(V) - print(io, typeof(t).name) - else - print(io, typeof(t)) - end - print(io, '(') - first = true - n = 0 - for (k, v) in t - first || print(io, ',') - first = false - show(io, k) - print(io, "=>") - show(io, v) - n+=1 - limit && n >= 10 && (print(io, "…"); break) - end - print(io, ')') + print(io, typeof(t)) + end + print(io, '(') + first = true + n = 0 + for (k, v) in t + first || print(io, ',') + first = false + show(recur_io, k) + print(io, "=>") + show(recur_io, v) + n+=1 + limit && n >= 10 && (print(io, "…"); break) end - return + print(io, ')') end + return + end - # Otherwise show more descriptively, with one line per key/value pair - rows, cols = sz - print(io, summary(t)) - isempty(t) && return - print(io, ":") - if limit - 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 -= 2 # Subtract the summary and final ⋮ continuation lines - - # determine max key width to align the output, caching the strings - ks = Array(AbstractString, min(rows, length(t))) - keylen = 0 - for (i, k) in enumerate(keys(t)) - i > rows && break - ks[i] = sprint(show, k) - keylen = clamp(length(ks[i]), keylen, div(cols, 3)) - end + # Otherwise show more descriptively, with one line per key/value pair + rows, cols = sz + print(io, summary(t)) + isempty(t) && return + print(io, ":") + if limit + 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 -= 2 # Subtract the summary and final ⋮ continuation lines + + # determine max key width to align the output, caching the strings + ks = Array(AbstractString, min(rows, length(t))) + keylen = 0 + for (i, k) in enumerate(keys(t)) + i > rows && break + ks[i] = sprint(0, show, k, env=recur_io) + keylen = clamp(length(ks[i]), keylen, div(cols, 3)) end + end - for (i, (k, v)) in enumerate(t) - print(io, "\n ") - limit && i > rows && (print(io, rpad("⋮", keylen), " => ⋮"); break) + for (i, (k, v)) in enumerate(t) + print(io, "\n ") + limit && i > rows && (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) - end - print(io, key) - print(io, " => ") + if limit + key = rpad(_truncate_at_width_or_chars(ks[i], keylen, "\r\n"), keylen) + else + key = sprint(0, show, k, env=recur_io) + end + print(recur_io, key) + print(io, " => ") - if limit - val = with_output_limit(()->sprint(show, v)) - val = _truncate_at_width_or_chars(val, cols - keylen, "\r\n") - print(io, val) - else - show(io, v) - end + if limit + val = sprint(0, show, v, env=recur_io) + val = _truncate_at_width_or_chars(val, cols - keylen, "\r\n") + print(io, val) + else + show(recur_io, v) end - finally - delete!(shown_set, t) end end @@ -158,8 +150,9 @@ summary{T<:Union{KeyIterator,ValueIterator}}(iter::T) = show(io::IO, iter::Union{KeyIterator,ValueIterator}) = show(io, collect(iter)) showkv(iter::Union{KeyIterator,ValueIterator}; kw...) = showkv(STDOUT, iter; kw...) -function showkv{T<:Union{KeyIterator,ValueIterator}}(io::IO, iter::T; limit::Bool = false, +function showkv{T<:Union{KeyIterator,ValueIterator}}(io::IO, iter::T; sz=(s = tty_size(); (s[1]-3, s[2]))) + limit::Bool = limit_output(io) rows, cols = sz print(io, summary(iter)) isempty(iter) && return @@ -176,7 +169,7 @@ function showkv{T<:Union{KeyIterator,ValueIterator}}(io::IO, iter::T; limit::Boo limit && i >= rows && (print(io, "⋮"); break) if limit - str = with_output_limit(()->sprint(show, v)) + str = sprint(0, show, v, env=io) str = _truncate_at_width_or_chars(str, cols, "\r\n") print(io, str) else diff --git a/base/docs/helpdb/Base.jl b/base/docs/helpdb/Base.jl index 16dd081420626..a7617d6604dba 100644 --- a/base/docs/helpdb/Base.jl +++ b/base/docs/helpdb/Base.jl @@ -3043,9 +3043,10 @@ Show an expression and result, returning the result. """ showcompact(x) + Show a more compact representation of a value. This is used for printing array elements. If -a new type has a different compact representation, it should overload `showcompact(io, x)` -where the first argument is a stream. +a new type has a different compact representation, +it should test `Base.limit_output(io)` in its normal `show` method. """ showcompact diff --git a/base/grisu.jl b/base/grisu.jl index 9215eea567d6d..112588858bab5 100644 --- a/base/grisu.jl +++ b/base/grisu.jl @@ -116,7 +116,7 @@ function _show(io::IO, x::AbstractFloat, mode, n::Int, typed, nanstr, infstr) nothing end -Base.show(io::IO, x::AbstractFloat) = _show(io, x, SHORTEST, 0, true) +Base.show(io::IO, x::AbstractFloat) = Base.limit_output(io) ? showcompact(io, x) : _show(io, x, SHORTEST, 0, true) Base.print(io::IO, x::Float32) = _show(io, x, SHORTEST, 0, false) Base.print(io::IO, x::Float16) = _show(io, x, SHORTEST, 0, false) diff --git a/base/interactiveutil.jl b/base/interactiveutil.jl index cf9347626c62c..b749a15db54a0 100644 --- a/base/interactiveutil.jl +++ b/base/interactiveutil.jl @@ -220,23 +220,19 @@ versioninfo(verbose::Bool) = versioninfo(STDOUT,verbose) # displaying type-ambiguity warnings function code_warntype(io::IO, f, t::ANY) - task_local_storage(:TYPEEMPHASIZE, true) - try - ct = code_typed(f, t) - for ast in ct - println(io, "Variables:") - vars = ast.args[2][1] - for v in vars - print(io, " ", v[1]) - show_expr_type(io, v[2]) - print(io, '\n') - end - print(io, "\nBody:\n ") - show_unquoted(io, ast.args[3], 2) - print(io, '\n') + emph_io = IOContext(io, :TYPEEMPHASIZE => true) + ct = code_typed(f, t) + for ast in ct + println(emph_io, "Variables:") + vars = ast.args[2][1] + for v in vars + print(emph_io, " ", v[1]) + show_expr_type(emph_io, v[2]) + print(emph_io, '\n') end - finally - task_local_storage(:TYPEEMPHASIZE, false) + print(emph_io, "\nBody:\n ") + show_unquoted(emph_io, ast.args[3], 2) + print(emph_io, '\n') end nothing end diff --git a/base/io.jl b/base/io.jl index 42c494cc66574..a5db9f90595f8 100644 --- a/base/io.jl +++ b/base/io.jl @@ -27,6 +27,39 @@ function eof end read(s::IO, ::Type{UInt8}) = error(typeof(s)," does not support byte I/O") write(s::IO, x::UInt8) = error(typeof(s)," does not support byte I/O") +# Generic wrappers around other IO objects +abstract AbstractPipe <: IO +function pipe_reader end +function pipe_writer end + +write(io::AbstractPipe, byte::UInt8) = write(pipe_writer(io), byte) +write(io::AbstractPipe, bytes::Vector{UInt8}) = write(pipe_writer(io), bytes) +write{T<:AbstractPipe}(io::T, args...) = write(pipe_writer(io), args...) +write{S<:AbstractPipe}(io::S, a::Array) = write(pipe_writer(io), a) +buffer_or_write(io::AbstractPipe, p::Ptr, n::Integer) = buffer_or_write(pipe_writer(io), p, n) +buffer_writes(io::AbstractPipe, args...) = buffer_writes(pipe_writer(io), args...) +flush(io::AbstractPipe) = flush(pipe_writer(io)) + +read(io::AbstractPipe, byte::Type{UInt8}) = read(pipe_reader(io), byte) +read!(io::AbstractPipe, bytes::Vector{UInt8}) = read!(pipe_reader(io), bytes) +read{T<:AbstractPipe}(io::T, args...) = read(pipe_reader(io), args...) +read!{T<:AbstractPipe}(io::T, args...) = read!(pipe_reader(io), args...) +readuntil{T<:AbstractPipe}(io::T, args...) = readuntil(pipe_reader(io), args...) +readbytes(io::AbstractPipe) = readbytes(pipe_reader(io)) +readavailable(io::AbstractPipe) = readavailable(pipe_reader(io)) + +isreadable(io::AbstractPipe) = isreadable(pipe_reader(io)) +iswritable(io::AbstractPipe) = iswritable(pipe_writer(io)) +isopen(io::AbstractPipe) = isopen(pipe_writer(io)) || isopen(pipe_reader(io)) +close(io::AbstractPipe) = (close(pipe_writer(io)); close(pipe_reader(io))) +wait_readnb(io::AbstractPipe, nb::Int) = wait_readnb(pipe_reader(io), nb) +wait_readbyte(io::AbstractPipe, byte::UInt8) = wait_readbyte(pipe_reader(io), byte) +wait_close(io::AbstractPipe) = (wait_close(pipe_writer(io)); wait_close(pipe_reader(io))) +nb_available(io::AbstractPipe) = nb_available(pipe_reader(io)) +eof(io::AbstractPipe) = eof(pipe_reader(io)) +reseteof(io::AbstractPipe) = reseteof(pipe_reader(io)) + + ## byte-order mark, ntoh & hton ## const ENDIAN_BOM = reinterpret(UInt32,UInt8[1:4;])[1] diff --git a/base/iostream.jl b/base/iostream.jl index b51b7adfaa5ab..5541af4b8a367 100644 --- a/base/iostream.jl +++ b/base/iostream.jl @@ -200,15 +200,6 @@ function takebuf_raw(s::IOStream) return buf, sz end -function sprint(size::Integer, f::Function, args...) - s = IOBuffer(Array(UInt8,size), true, true) - truncate(s,0) - f(s, args...) - takebuf_string(s) -end - -sprint(f::Function, args...) = sprint(0, f, args...) - write(x) = write(STDOUT::IO, x) function readuntil(s::IOStream, delim::UInt8) diff --git a/base/irrationals.jl b/base/irrationals.jl index b61b8b2ba8734..1c31ba4b98aa4 100644 --- a/base/irrationals.jl +++ b/base/irrationals.jl @@ -130,8 +130,8 @@ log(::Irrational{:e}) = 1 # use 1 to correctly promote expressions like log(x)/l log(::Irrational{:e}, x) = log(x) # align along = for nice Array printing -function alignment(x::Irrational) - m = match(r"^(.*?)(=.*)$", sprint(showcompact_lim, x)) - m === nothing ? (length(sprint(showcompact_lim, x)), 0) : +function alignment(io::IO, x::Irrational) + m = match(r"^(.*?)(=.*)$", sprint(0, showcompact_lim, x, env=io)) + m === nothing ? (length(sprint(0, showcompact_lim, x, env=io)), 0) : (length(m.captures[1]), length(m.captures[2])) end diff --git a/base/methodshow.jl b/base/methodshow.jl index 9050c83fdaf8c..e57f0521bba84 100644 --- a/base/methodshow.jl +++ b/base/methodshow.jl @@ -181,4 +181,4 @@ end # override usual show method for Vector{Method}: don't abbreviate long lists writemime(io::IO, mime::MIME"text/plain", mt::AbstractVector{Method}) = - showarray(io, mt, limit=false) + showarray(IOContext(io, :limit_output => false), mt) diff --git a/base/mpfr.jl b/base/mpfr.jl index 8129b3ef92594..0dcc262a46c39 100644 --- a/base/mpfr.jl +++ b/base/mpfr.jl @@ -13,7 +13,7 @@ import exp, exp2, exponent, factorial, floor, fma, hypot, isinteger, isfinite, isinf, isnan, ldexp, log, log2, log10, max, min, mod, modf, nextfloat, prevfloat, promote_rule, rem, round, show, - showcompact, sum, sqrt, string, print, trunc, precision, exp10, expm1, + sum, sqrt, string, print, trunc, precision, exp10, expm1, gamma, lgamma, digamma, erf, erfc, zeta, eta, log1p, airyai, eps, signbit, sin, cos, tan, sec, csc, cot, acos, asin, atan, cosh, sinh, tanh, sech, csch, coth, acosh, asinh, atanh, atan2, @@ -863,7 +863,6 @@ end print(io::IO, b::BigFloat) = print(io, string(b)) show(io::IO, b::BigFloat) = print(io, string(b)) -showcompact(io::IO, b::BigFloat) = print(io, string(b)) # get/set exponent min/max get_emax() = ccall((:mpfr_get_emax, :libmpfr), Clong, ()) diff --git a/base/precompile.jl b/base/precompile.jl index 337aa70c543c3..307545932f484 100644 --- a/base/precompile.jl +++ b/base/precompile.jl @@ -172,7 +172,7 @@ precompile(Base.abspath, (UTF8String, UTF8String)) precompile(Base.abspath, (UTF8String,)) precompile(Base.abspath, (ASCIIString, ASCIIString)) precompile(Base.abspath, (ASCIIString,)) -precompile(Base.alignment, (Float64,)) +precompile(Base.alignment, (Base.IOContext, Float64,)) precompile(Base.any, (Function, Array{Any,1})) precompile(Base.arg_gen, (ASCIIString,)) precompile(Base.associate_julia_struct, (Ptr{Void}, Base.TTY)) @@ -379,7 +379,7 @@ precompile(Base.setindex!, (Vector{Any}, Vector{Any}, Int)) precompile(Base.show, (Base.Terminals.TTYTerminal, Int)) precompile(Base.show, (Float64,)) precompile(Base.show, (IOStream, Int32)) -precompile(Base.showlimited, (Base.Terminals.TTYTerminal, Int)) +precompile(Base.showcompact, (Base.Terminals.TTYTerminal, Int)) precompile(Base.similar, (Array{Base.LineEdit.Prompt, 1}, Type{Base.LineEdit.TextInterface}, Tuple{Int})) precompile(Base.size, (Base.Terminals.TTYTerminal,)) precompile(Base.sizehint!, (Base.Dict{Symbol, Any}, Int)) diff --git a/base/range.jl b/base/range.jl index 55094ae93a56f..396f5d9a307cb 100644 --- a/base/range.jl +++ b/base/range.jl @@ -278,7 +278,7 @@ function print_range(io::IO, r::Range, maxpossiblecols = div(screenwidth, 1+sepsize) # assume each element is at least 1 char + 1 separator colsr = n <= maxpossiblecols ? (1:n) : [1:div(maxpossiblecols,2)+1; (n-div(maxpossiblecols,2)):n] rowmatrix = r[colsr]' # treat the range as a one-row matrix for print_matrix_row - A = alignment(rowmatrix,1:m,1:length(rowmatrix),screenwidth,screenwidth,sepsize) # how much space range takes + A = alignment(io, rowmatrix, 1:m, 1:length(rowmatrix), screenwidth, screenwidth, sepsize) # how much space range takes if n <= length(A) # cols fit screen, so print out all elements print(io, pre) # put in pre chars print_matrix_row(io,rowmatrix,A,1,1:n,sep) # the entire range @@ -287,9 +287,9 @@ function print_range(io::IO, r::Range, # how many chars left after dividing width of screen in half # and accounting for the horiz ellipsis c = div(screenwidth-length(hdots)+1,2)+1 # chars remaining for each side of rowmatrix - alignR = reverse(alignment(rowmatrix,1:m,length(rowmatrix):-1:1,c,c,sepsize)) # which cols of rowmatrix to put on the right + alignR = reverse(alignment(io, rowmatrix, 1:m, length(rowmatrix):-1:1, c, c, sepsize)) # which cols of rowmatrix to put on the right c = screenwidth - sum(map(sum,alignR)) - (length(alignR)-1)*sepsize - length(hdots) - alignL = alignment(rowmatrix,1:m,1:length(rowmatrix),c,c,sepsize) # which cols of rowmatrix to put on the left + alignL = alignment(io, rowmatrix, 1:m, 1:length(rowmatrix), c, c, sepsize) # which cols of rowmatrix to put on the left print(io, pre) # put in pre chars print_matrix_row(io, rowmatrix,alignL,1,1:length(alignL),sep) # left part of range print(io, hdots) # horizontal ellipsis diff --git a/base/replutil.jl b/base/replutil.jl index 52cb8e4701bf8..c0a99ea3fc85d 100644 --- a/base/replutil.jl +++ b/base/replutil.jl @@ -1,7 +1,8 @@ # This file is a part of Julia. License is MIT: http://julialang.org/license # fallback text/plain representation of any type: -writemime(io::IO, ::MIME"text/plain", x) = showlimited(io, x) +writemime(io::IO, ::MIME"text/plain", x) = showcompact(io, x) +writemime(io::IO, ::MIME"text/plain", x::Number) = show(io, x) function writemime(io::IO, ::MIME"text/plain", f::Function) if isgeneric(f) @@ -22,7 +23,7 @@ function writemime(io::IO, ::MIME"text/plain", r::Range) print(io, summary(r)) if !isempty(r) println(io, ":") - with_output_limit(()->print_range(io, r)) + print_range(IOContext(io, :limit_output => true), r) end end @@ -30,23 +31,18 @@ function writemime(io::IO, ::MIME"text/plain", v::AbstractVector) print(io, summary(v)) if !isempty(v) println(io, ":") - with_output_limit(()->print_matrix(io, v)) + print_matrix(IOContext(io, :limit_output => true), v) end end writemime(io::IO, ::MIME"text/plain", v::AbstractArray) = - with_output_limit(()->showarray(io, v, header=true, repr=false)) + showarray(IOContext(io, :limit_output => true), v, header=true, repr=false) function writemime(io::IO, ::MIME"text/plain", v::DataType) show(io, v) # TODO: maybe show constructor info? end -writemime(io::IO, ::MIME"text/plain", t::Associative) = - showdict(io, t, limit=true) -writemime(io::IO, ::MIME"text/plain", t::Union{KeyIterator, ValueIterator}) = - showkv(io, t, limit=true) - function writemime(io::IO, ::MIME"text/plain", t::Task) show(io, t) if t.state == :failed diff --git a/base/show.jl b/base/show.jl index acce69fc33ef0..1390be925408c 100644 --- a/base/show.jl +++ b/base/show.jl @@ -1,8 +1,67 @@ # This file is a part of Julia. License is MIT: http://julialang.org/license show(x) = show(STDOUT::IO, x) +print(io::IO, s::Symbol) = (write(io,s); nothing) + +immutable IOContext{IO_t <: IO} <: AbstractPipe + io::IO_t + dict::ImmutableDict{Symbol, Any} + function IOContext(io::IO_t, dict::ImmutableDict{Symbol, Any}) + assert(!(IO_t <: IOContext)) + return new(io, dict) + end +end + +""" + IOContext{<:IO} <: IO + +IOContext provides a mechanism for passing output-configuration keyword arguments through arbitrary show methods. + +In short, it is an immutable Dictionary that is a subclass of IO. + + IOContext(io::IO, KV::Pair) + +Create a new entry in the IO Dictionary for the key => value pair + + - use `(key => value) in dict` to see if this particular combination is in the properties set + - use `get(dict, key, default)` to retrieve the most recent value for a particular key + + IOContext(io::IO, context::IOContext) + +Create a IOContext that wraps an alternate IO but inherits the keyword arguments from the context +""" +IOContext -print(io::IO, s::Symbol) = (write(io,s);nothing) +IOContext(io::IOContext) = io +IOContext(io::IO) = IOContext(io, ImmutableDict{Symbol,Any}()) + +IOContext(io::IOContext, dict::ImmutableDict) = typeof(io)(io.io, dict) +IOContext(io::IO, dict::ImmutableDict) = IOContext{typeof(io)}(io, dict) + +IOContext(io::IO, key, value) = IOContext(io, ImmutableDict{Symbol, Any}(key, value)) +IOContext(io::IOContext, key, value) = IOContext(io, ImmutableDict{Symbol, Any}(io.dict, key, value)) + +IOContext(io::IO, context::IO) = IOContext(io) +IOContext(io::IO, context::IOContext) = IOContext(io, context.dict) +IOContext(io::IO, KV::Pair) = IOContext(io, KV[1], KV[2]) + +show(io::IO, ctx::IOContext) = (print(io, "IOContext("); show(io, ctx.io); print(io, ")")) + +pipe_reader(io::IOContext) = io.io +pipe_writer(io::IOContext) = io.io +lock(io::IOContext) = lock(io.io) +unlock(io::IOContext) = unlock(io.io) + +in(key_value::Pair, io::IOContext) = in(key_value, io.dict, is) +in(key_value::Pair, io::IO) = false +haskey(io::IOContext, key) = haskey(io.dict, key) +haskey(io::IO, key) = false +getindex(io::IOContext, key) = getindex(io.dict, key) +getindex(io::IO, key) = throw(KeyError(key)) +get(io::IOContext, key, default) = get(io.dict, key, default) +get(io::IO, key, default) = default + +limit_output(io::IO) = get(io, :limit_output, false) === true show(io::IO, x::ANY) = show_default(io, x) function show_default(io::IO, x::ANY) @@ -11,37 +70,21 @@ function show_default(io::IO, x::ANY) print(io, '(') nf = nfields(t) if nf != 0 || t.size==0 - recorded = false - shown_set = get(task_local_storage(), :SHOWNSET, nothing) - if shown_set === nothing - shown_set = ObjectIdDict() - task_local_storage(:SHOWNSET, shown_set) - end - - try - if x in keys(shown_set) - print(io, "#= circular reference =#") - else - shown_set[x] = true - recorded = true - - for i=1:nf - f = fieldname(t, i) - if !isdefined(x, f) - print(io, undef_ref_str) - else - show(io, x.(f)) - end - if i < nf - print(io, ',') - end + if (:SHOWN_SET => x) in io + print(io, "#= circular reference =#") + else + recur_io = IOContext(io, :SHOWN_SET => x) + for i=1:nf + f = fieldname(t, i) + if !isdefined(x, f) + print(io, undef_ref_str) + else + show(recur_io, x.(f)) + end + if i < nf + print(io, ',') end end - catch e - rethrow(e) - - finally - if recorded; delete!(shown_set, x); end end else nb = t.size @@ -114,12 +157,6 @@ function show(io::IO, x::DataType) end end -showcompact(io::IO, x) = show(io, x) -showcompact(x) = showcompact(STDOUT::IO, x) - -showcompact_lim(io, x) = _limit_output ? showcompact(io, x) : show(io, x) -showcompact_lim(io, x::Number) = _limit_output ? showcompact(io, x) : print(io, x) - macro show(exs...) blk = Expr(:block) for ex in exs @@ -175,7 +212,7 @@ function show(io::IO, l::LambdaStaticData) print(io, ")") end -function show_delim_array(io::IO, itr::AbstractArray, op, delim, cl, delim_one, compact=false, i1=1, l=length(itr)) +function show_delim_array(io::IO, itr::AbstractArray, op, delim, cl, delim_one, i1=1, l=length(itr)) print(io, op) newline = true first = true @@ -191,10 +228,8 @@ function show_delim_array(io::IO, itr::AbstractArray, op, delim, cl, delim_one, newline && multiline && println(io) if !isbits(x) && is(x, itr) print(io, "#= circular reference =#") - elseif compact - showcompact_lim(io, x) else - show(io, x) + showcompact_lim(io, x) end end i += 1 @@ -215,7 +250,7 @@ function show_delim_array(io::IO, itr::AbstractArray, op, delim, cl, delim_one, print(io, cl) end -function show_delim_array(io::IO, itr, op, delim, cl, delim_one, compact=false, i1=1, n=typemax(Int)) +function show_delim_array(io::IO, itr, op, delim, cl, delim_one, i1=1, n=typemax(Int)) print(io, op) state = start(itr) newline = true @@ -358,6 +393,8 @@ end ## AST printing helpers ## +typeemphasize(io::IO) = get(io, :TYPEEMPHASIZE, false) === true + const indent_width = 4 function show_expr_type(io::IO, ty) @@ -366,7 +403,7 @@ function show_expr_type(io::IO, ty) elseif is(ty, IntrinsicFunction) print(io, "::I") else - emph = get(task_local_storage(), :TYPEEMPHASIZE, false)::Bool + emph = typeemphasize(io) if emph && !isleaftype(ty) emphasize(io, "::$ty") else @@ -494,7 +531,6 @@ end function show_unquoted(io::IO, ex::Expr, indent::Int, prec::Int) head, args, nargs = ex.head, ex.args, length(ex.args) show_type = true - emphstate = get(task_local_storage(), :TYPEEMPHASIZE, false) # dot (i.e. "x.y") if is(head, :(.)) show_unquoted(io, args[1], indent + indent_width) @@ -545,7 +581,7 @@ function show_unquoted(io::IO, ex::Expr, indent::Int, prec::Int) if (in(ex.args[1], (GlobalRef(Base, :box), TopNode(:box), :throw)) || ismodulecall(ex) || (ex.typ === Any && is_intrinsic_expr(ex.args[1]))) - show_type = task_local_storage(:TYPEEMPHASIZE, false) + show_type = typeemphasize(io) end # scalar multiplication (i.e. "100x") @@ -721,18 +757,21 @@ function show_unquoted(io::IO, ex::Expr, indent::Int, prec::Int) show_unquoted(io, args[2], indent+indent_width) elseif is(head, :string) - a = map(args) do x + print(io, '"') + for x in args if !isa(x,AbstractString) + print(io, "\$(") if isa(x,Symbol) && !(x in quoted_syms) - string("\$(", x, ")") + print(io, x) else - string("\$(", sprint(show_unquoted,x), ")") + show_unquoted(io, x) end + print(io, ")") else - sprint(print_escaped, x, "\"\$") + print_escaped(io, x, "\"\$") end end - print(io, '"', a..., '"') + print(io, '"') elseif (is(head, :&)#= || is(head, :$)=#) && length(args) == 1 print(io, head) @@ -771,9 +810,10 @@ function show_unquoted(io::IO, ex::Expr, indent::Int, prec::Int) # print anything else as "Expr(head, args...)" else show_type = false - emph = get(task_local_storage(), :TYPEEMPHASIZE, false)::Bool && - (ex.head === :lambda || ex.head == :method) - task_local_storage(:TYPEEMPHASIZE, emph) + emphstate = typeemphasize(io) + if emphstate && ex.head !== :lambda && ex.head !== :method + io = IOContext(io, :TYPEEMPHASIZE => false) + end print(io, "\$(Expr(") show(io, ex.head) for arg in args @@ -789,7 +829,6 @@ function show_unquoted(io::IO, ex::Expr, indent::Int, prec::Int) show_type = false end show_type && show_expr_type(io, ex.typ) - task_local_storage(:TYPEEMPHASIZE, emphstate) end function ismodulecall(ex::Expr) @@ -982,25 +1021,25 @@ dump(io::IO, x::TypeVar, n::Int, indent) = println(io, x.name) `alignment(X)` returns a tuple (left,right) showing how many characters are needed on either side of an alignment feature such as a decimal point. """ -alignment(x::Any) = (0, length(sprint(showcompact_lim, x))) -alignment(x::Number) = (length(sprint(showcompact_lim, x)), 0) +alignment(io::IO, x::Any) = (0, length(sprint(0, showcompact_lim, x, env=io))) +alignment(io::IO, x::Number) = (length(sprint(0, showcompact_lim, x, env=io)), 0) "`alignment(42)` yields (2,0)" -alignment(x::Integer) = (length(sprint(showcompact_lim, x)), 0) +alignment(io::IO, x::Integer) = (length(sprint(0, showcompact_lim, x, env=io)), 0) "`alignment(4.23)` yields (1,3) for `4` and `.23`" -function alignment(x::Real) - m = match(r"^(.*?)((?:[\.eE].*)?)$", sprint(showcompact_lim, x)) - m === nothing ? (length(sprint(showcompact_lim, x)), 0) : +function alignment(io::IO, x::Real) + m = match(r"^(.*?)((?:[\.eE].*)?)$", sprint(0, showcompact_lim, x, env=io)) + m === nothing ? (length(sprint(0, showcompact_lim, x, env=io)), 0) : (length(m.captures[1]), length(m.captures[2])) end "`alignment(1 + 10im)` yields (3,5) for `1 +` and `_10im` (plus sign on left, space on right)" -function alignment(x::Complex) - m = match(r"^(.*[\+\-])(.*)$", sprint(showcompact_lim, x)) - m === nothing ? (length(sprint(showcompact_lim, x)), 0) : +function alignment(io::IO, x::Complex) + m = match(r"^(.*[\+\-])(.*)$", sprint(0, showcompact_lim, x, env=io)) + m === nothing ? (length(sprint(0, showcompact_lim, x, env=io)), 0) : (length(m.captures[1]), length(m.captures[2])) end -function alignment(x::Rational) - m = match(r"^(.*?/)(/.*)$", sprint(showcompact_lim, x)) - m === nothing ? (length(sprint(showcompact_lim, x)), 0) : +function alignment(io::IO, x::Rational) + m = match(r"^(.*?/)(/.*)$", sprint(0, showcompact_lim, x, env=io)) + m === nothing ? (length(sprint(0, showcompact_lim, x, env=io)), 0) : (length(m.captures[1]), length(m.captures[2])) end @@ -1019,7 +1058,7 @@ Alignment is reported as a vector of (left,right) tuples, one for each column going across the screen. """ function alignment( - X::AbstractVecOrMat, + io::IO, X::AbstractVecOrMat, rows::AbstractVector, cols::AbstractVector, cols_if_complete::Integer, cols_otherwise::Integer, sep::Integer ) @@ -1028,7 +1067,7 @@ function alignment( l = r = 0 for i in rows # plumb down and see what largest element sizes are if isassigned(X,i,j) - aij = alignment(X[i,j]) + aij = alignment(io, X[i,j]) else aij = undef_ref_alignment end @@ -1064,8 +1103,8 @@ function print_matrix_row(io::IO, j = cols[k] if isassigned(X,Int(i),Int(j)) # isassigned accepts only `Int` indices x = X[i,j] - a = alignment(x) - sx = sprint(showcompact_lim, x) + a = alignment(io, x) + sx = sprint(0, showcompact_lim, x, env=io) else a = undef_ref_alignment sx = undef_ref_str @@ -1135,7 +1174,7 @@ function print_matrix(io::IO, X::AbstractVecOrMat, # 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) + 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 @@ -1147,9 +1186,9 @@ function print_matrix(io::IO, X::AbstractVecOrMat, 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(X,rowsA,reverse(colsA),c,c,sepsize)) # alignments for right + 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(X,rowsA,colsA,c,c,sepsize) # alignments for left of ellipsis + Lalign = alignment(io, 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,Lalign,i,1:length(Lalign),sep) @@ -1174,9 +1213,9 @@ function print_matrix(io::IO, X::AbstractVecOrMat, 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(X,rowsA,reverse(colsA),c,c,sepsize)) + 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(X,rowsA,colsA,c,c,sepsize) + 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 == 1 ? pre : presp) @@ -1259,11 +1298,6 @@ function show_nd(io::IO, a::AbstractArray, limit, print_matrix, label_slices) end end -# global flag for limiting output -# TODO: this should be replaced with a better mechanism. currently it is only -# for internal use in showing arrays. -_limit_output = false - """ `print_matrix_repr(io, X)` prints matrix X with opening and closing square brackets. """ @@ -1295,10 +1329,12 @@ end # array output. Not sure I want to do it this way. showarray(X::AbstractArray; kw...) = showarray(STDOUT, X; kw...) function showarray(io::IO, X::AbstractArray; - header::Bool=true, limit::Bool=_limit_output, - sz = (s = tty_size(); (s[1]-4, s[2])), repr=false) + header::Bool=true, + sz = (s = tty_size(); (s[1]-4, s[2])), + repr=false) rows, cols = sz header && print(io, summary(X)) + limit::Bool = limit_output(io) if !isempty(X) header && println(io, ":") if ndims(X) == 0 @@ -1331,38 +1367,27 @@ function showarray(io::IO, X::AbstractArray; end end -show(io::IO, X::AbstractArray) = showarray(io, X, header=_limit_output, repr=!_limit_output) - -function with_output_limit(thk, lim=true) # thk is usually show() - global _limit_output - last = _limit_output - _limit_output = lim - try - thk() - finally - _limit_output = last - end -end +show(io::IO, X::AbstractArray) = showarray(io, X, header=limit_output(io), repr=!limit_output(io)) showall(x) = showall(STDOUT, x) function showall(io::IO, x) - if _limit_output==false + if !limit_output(io) show(io, x) else - with_output_limit(false) do - show(io, x) - end + show(IOContext(io, :limit_output => false), x) end end -showlimited(x) = showlimited(STDOUT, x) -function showlimited(io::IO, x) - if _limit_output==true +# TODO: deprecated. remove this once methods for showcompact are gone +showcompact_lim(io, x) = limit_output(io) ? showcompact(io, x) : show(io, x) +showcompact_lim(io, x::Number) = limit_output(io) ? showcompact(io, x) : print(io, x) + +showcompact(x) = showcompact(STDOUT, x) +function showcompact(io::IO, x) + if limit_output(io) show(io, x) else - with_output_limit(true) do - show(io, x) - end + show(IOContext(io, :limit_output => true), x) end end @@ -1381,12 +1406,16 @@ end function show_vector(io::IO, v, opn, cls) compact, prefix = array_eltype_show_how(v) + limited = limit_output(io) + if limited && !compact + io = IOContext(io, :limit_output => false) + end print(io, prefix) - if _limit_output && length(v) > 20 - show_delim_array(io, v, opn, ",", "", false, compact, 1, 10) + if limited && length(v) > 20 + show_delim_array(io, v, opn, ",", "", false, 1, 10) print(io, " \u2026 ") n = length(v) - show_delim_array(io, v, "", ",", cls, false, compact, n-9, 10) + show_delim_array(io, v, "", ",", cls, false, n-9, 10) else show_delim_array(io, v, opn, ",", cls, false) end diff --git a/base/sparse/sparsematrix.jl b/base/sparse/sparsematrix.jl index ac7216f97f234..6ecb1edf6c3c2 100644 --- a/base/sparse/sparsematrix.jl +++ b/base/sparse/sparsematrix.jl @@ -77,14 +77,14 @@ convenient iterating over a sparse matrix : nzrange(S::SparseMatrixCSC, col::Integer) = S.colptr[col]:(S.colptr[col+1]-1) function Base.showarray(io::IO, S::SparseMatrixCSC; - header::Bool=true, limit::Bool=Base._limit_output, + header::Bool=true, rows = Base.tty_size()[1], repr=false) # TODO: repr? - if header print(io, S.m, "x", S.n, " sparse matrix with ", nnz(S), " ", eltype(S), " entries:") end + limit::Bool = Base.limit_output(io) if limit half_screen_rows = div(rows - 8, 2) else @@ -97,7 +97,7 @@ function Base.showarray(io::IO, S::SparseMatrixCSC; if k < half_screen_rows || k > nnz(S)-half_screen_rows print(io, sep, '[', rpad(S.rowval[k], pad), ", ", lpad(col, pad), "] = ") if isassigned(S.nzval, k) - showcompact(io, S.nzval[k]) + Base.showcompact_lim(io, S.nzval[k]) else print(io, Base.undef_ref_str) end diff --git a/base/sparse/sparsevector.jl b/base/sparse/sparsevector.jl index 06ae8c16a7093..4d5bdbaacef10 100644 --- a/base/sparse/sparsevector.jl +++ b/base/sparse/sparsevector.jl @@ -585,7 +585,7 @@ getindex(x::AbstractSparseVector, ::Colon) = copy(x) ### show and friends function showarray(io::IO, x::AbstractSparseVector; - header::Bool=true, limit::Bool=Base._limit_output, + header::Bool=true, rows = Base.tty_size()[1], repr=false) n = length(x) @@ -596,13 +596,15 @@ function showarray(io::IO, x::AbstractSparseVector; if header println(io, summary(x)) end + + limit::Bool = Base.limit_output(io) half_screen_rows = limit ? div(rows - 8, 2) : typemax(Int) pad = ndigits(n) sep = "\n\t" for k = 1:length(nzind) if k < half_screen_rows || k > xnnz - half_screen_rows print(io, " ", '[', rpad(nzind[k], pad), "] = ") - showcompact(io, nzval[k]) + Base.showcompact_lim(io, nzval[k]) println(io) elseif k == half_screen_rows println(io, " ", " "^pad, " \u22ee") @@ -616,7 +618,7 @@ function summary(x::AbstractSparseVector) end show(io::IO, x::AbstractSparseVector) = showarray(io, x) -writemime(io::IO, ::MIME"text/plain", x::AbstractSparseVector) = Base.with_output_limit(()->show(io, x)) +writemime(io::IO, ::MIME"text/plain", x::AbstractSparseVector) = show(IOContext(io, :limit_output => true), x) ### Conversion to matrix diff --git a/base/stream.jl b/base/stream.jl index 846774f559423..b97c9a1892f4a 100644 --- a/base/stream.jl +++ b/base/stream.jl @@ -529,11 +529,6 @@ end # (composed of two half-pipes: .in and .out) ########################################## -# allows sharing implementation of wrappers around other IO objects -abstract AbstractPipe <: IO -function pipe_reader end -function pipe_writer end - type Pipe <: AbstractPipe in::PipeEndpoint # writable out::PipeEndpoint # readable @@ -554,33 +549,6 @@ show(io::IO, stream::Pipe) = print(io, uv_status_string(stream.out), ", ", nb_available(stream), " bytes waiting)") -write(io::AbstractPipe, byte::UInt8) = write(pipe_writer(io), byte) -write(io::AbstractPipe, bytes::Vector{UInt8}) = write(pipe_writer(io), bytes) -write{T<:AbstractPipe}(io::T, args...) = write(pipe_writer(io), args...) -write{S<:AbstractPipe}(io::S, a::Array) = write(pipe_writer(io), a) -buffer_or_write(io::AbstractPipe, p::Ptr, n::Integer) = buffer_or_write(pipe_writer(io), p, n) -buffer_writes(io::AbstractPipe, args...) = buffer_writes(pipe_writer(io), args...) -flush(io::AbstractPipe) = flush(pipe_writer(io)) - -read(io::AbstractPipe, byte::Type{UInt8}) = read(pipe_reader(io), byte) -read!(io::AbstractPipe, bytes::Vector{UInt8}) = read!(pipe_reader(io), bytes) -read{T<:AbstractPipe}(io::T, args...) = read(pipe_reader(io), args...) -read!{T<:AbstractPipe}(io::T, args...) = read!(pipe_reader(io), args...) -readuntil{T<:AbstractPipe}(io::T, args...) = readuntil(pipe_reader(io), args...) -readbytes(io::AbstractPipe) = readbytes(pipe_reader(io)) -readavailable(io::AbstractPipe) = readavailable(pipe_reader(io)) - -isreadable(io::AbstractPipe) = isreadable(pipe_reader(io)) -iswritable(io::AbstractPipe) = iswritable(pipe_writer(io)) -isopen(io::AbstractPipe) = isopen(pipe_writer(io)) || isopen(pipe_reader(io)) -close(io::AbstractPipe) = (close(pipe_writer(io)); close(pipe_reader(io))) -wait_readnb(io::AbstractPipe, nb::Int) = wait_readnb(pipe_reader(io), nb) -wait_readbyte(io::AbstractPipe, byte::UInt8) = wait_readbyte(pipe_reader(io), byte) -wait_close(io::AbstractPipe) = (wait_close(pipe_writer(io)); wait_close(pipe_reader(io))) -nb_available(io::AbstractPipe) = nb_available(pipe_reader(io)) -eof(io::AbstractPipe) = eof(pipe_reader(io)) -reseteof(io::AbstractPipe) = reseteof(pipe_reader(io)) - ########################################## # Async Worker ########################################## diff --git a/base/strings/io.jl b/base/strings/io.jl index e78cb5ddcd6ce..bf3d4b4a09e61 100644 --- a/base/strings/io.jl +++ b/base/strings/io.jl @@ -29,17 +29,40 @@ println(xs...) = println(STDOUT, xs...) ## conversion of general objects to strings ## -function print_to_string(xs...) +function sprint(size::Integer, f::Function, args...; env=nothing) + s = IOBuffer(Array(UInt8,size), true, true) + truncate(s,0) + if env !== nothing + f(IOContext(s, env), args...) + else + f(s, args...) + end + d = s.data + resize!(d,s.size) + bytestring(d) +end +sprint(f::Function, args...) = sprint(0, f, args...) + +function print_to_string(xs...; env=nothing) # specialized for performance reasons s = IOBuffer(Array(UInt8,isa(xs[1],AbstractString) ? endof(xs[1]) : 0), true, true) - for x in xs - print(s, x) + truncate(s,0) + if env !== nothing + env_io = IOContext(s, env) + for x in xs + print(env_io, x) + end + else + for x in xs + print(s, x) + end end d = s.data resize!(d,s.size) bytestring(d) end +string_with_env(env, xs...) = print_to_string(xs...; env=env) string(xs...) = print_to_string(xs...) bytestring(s::AbstractString...) = print_to_string(s...) diff --git a/base/unicode/utf8proc.jl b/base/unicode/utf8proc.jl index 4b8ee196cb9fb..d098ce3c993da 100644 --- a/base/unicode/utf8proc.jl +++ b/base/unicode/utf8proc.jl @@ -3,7 +3,7 @@ # Various Unicode functionality from the utf8proc library module UTF8proc -import Base: show, showcompact, ==, hash, string, symbol, isless, length, eltype, start, next, done, convert, isvalid, lowercase, uppercase +import Base: show, ==, hash, string, symbol, isless, length, eltype, start, next, done, convert, isvalid, lowercase, uppercase export isgraphemebreak diff --git a/doc/manual/networking-and-streams.rst b/doc/manual/networking-and-streams.rst index 8f15f53e828bb..f19598688da48 100644 --- a/doc/manual/networking-and-streams.rst +++ b/doc/manual/networking-and-streams.rst @@ -102,6 +102,11 @@ the difference between the two):: julia> print(STDOUT,0x61) 97 +IO Output Contextual Properties +------------------------------- + +Sometimes IO output can benefit from the ability to pass contextual information into show methods. The ``IOContext`` object provides this framework for associating arbitrary metadata with an IO object. For example, ``showlimited`` adds a hinting parameter to the IO object that the invoked show method should print a shorter output (if applicable). + Working with Files ------------------ diff --git a/doc/stdlib/io-network.rst b/doc/stdlib/io-network.rst index 6e6d40c0835cc..807bfe2b73ab3 100644 --- a/doc/stdlib/io-network.rst +++ b/doc/stdlib/io-network.rst @@ -368,6 +368,29 @@ General I/O Read all available data on the stream, blocking the task only if no data is available. The result is a ``Vector{UInt8,1}``\ . +.. function:: IOContext{<:IO} <: IO + + .. Docstring generated from Julia source + + IOContext provides a mechanism for passing output-configuration keyword arguments through arbitrary show methods. + + In short, it is an immutable Dictionary that is a subclass of IO. + + .. code-block:: julia + + IOContext(io::IO, KV::Pair) + + Create a new entry in the IO Dictionary for the key => value pair + + * use ``(key => value) in dict`` to see if this particular combination is in the properties set + * use ``get(dict, key, default)`` to retrieve the most recent value for a particular key + + .. code-block:: julia + + IOContext(io::IO, context::IOContext) + + Create a IOContext that wraps an alternate IO but inherits the keyword arguments from the context + Text I/O -------- diff --git a/test/dict.jl b/test/dict.jl index 59fa26d4835c9..3908f42a287c9 100644 --- a/test/dict.jl +++ b/test/dict.jl @@ -257,7 +257,7 @@ for d in (Dict("\n" => "\n", "1" => "\n", "\n" => "2"), for cols in (12, 40, 80), rows in (2, 10, 24) # Ensure output is limited as requested s = IOBuffer() - Base.showdict(s, d, limit=true, sz=(rows, cols)) + Base.showdict(Base.IOContext(s, :limit_output => true), d, sz=(rows, cols)) out = split(takebuf_string(s),'\n') for line in out[2:end] @test strwidth(line) <= cols @@ -266,7 +266,7 @@ for d in (Dict("\n" => "\n", "1" => "\n", "\n" => "2"), for f in (keys, values) s = IOBuffer() - Base.showkv(s, f(d), limit=true, sz=(rows, cols)) + Base.showkv(Base.IOContext(s, :limit_output => true), f(d), sz=(rows, cols)) out = split(takebuf_string(s),'\n') for line in out[2:end] @test strwidth(line) <= cols @@ -275,7 +275,7 @@ for d in (Dict("\n" => "\n", "1" => "\n", "\n" => "2"), end end # Simply ensure these do not throw errors - Base.showdict(IOBuffer(), d, limit=false) + Base.showdict(IOBuffer(), d) @test !isempty(summary(d)) @test !isempty(summary(keys(d))) @test !isempty(summary(values(d))) @@ -285,7 +285,7 @@ end type Alpha end Base.show(io::IO, ::Alpha) = print(io,"α") sbuff = IOBuffer() -Base.showdict(sbuff, Dict(Alpha()=>1), limit=true, sz=(10,20)) +Base.showdict(Base.IOContext(sbuff, :limit_output => true), Dict(Alpha()=>1), sz=(10,20)) @test !contains(bytestring(sbuff), "…") # issue #2540 @@ -337,8 +337,9 @@ let a[3] = T10647(a) @test a == a show(IOBuffer(), a) + Base.show(Base.IOContext(IOBuffer(), :limit_output => true), a) Base.showdict(IOBuffer(), a) - Base.showdict(IOBuffer(), a; limit=true) + Base.showdict(Base.IOContext(IOBuffer(), :limit_output => true), a) end From fff2b9aa811549186ec39e71cc4f6331203e2119 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Thu, 29 Oct 2015 17:22:13 -0400 Subject: [PATCH 4/6] modify methodshow and helper methods to feed a tvar_env into the printing of a method signature for example, this turns: rand{T<:Union{Bool,Int16,Int32,Int64}}(::Type{T<:Union{Bool,Int16,Int32,Int64}}) at random.jl:38 into: rand{T<:Union{Bool,Int16,Int32,Int64}}(::Type{T}) at random.jl:38 and it resolves ambiguous expressions such as: foo{T,N}(::Array{T,N}, ::Vector, ::Array) which used to be printed as: foo{T,N}(::Array{T,N}, ::Array{T,1}, ::Array{T,N}) by printing it instead as: foo{T,N}(::Array{T,N}, ::Array{T<:Any,1}, ::Array{T<:Any,N<:Any}) --- base/expr.jl | 12 ------------ base/methodshow.jl | 13 +++++++------ base/show.jl | 20 ++++++++++++++++++++ 3 files changed, 27 insertions(+), 18 deletions(-) diff --git a/base/expr.jl b/base/expr.jl index 33660726c91cf..d5fa036cdf10c 100644 --- a/base/expr.jl +++ b/base/expr.jl @@ -45,18 +45,6 @@ astcopy(x) = x ==(x::Expr, y::Expr) = x.head === y.head && x.args == y.args ==(x::QuoteNode, y::QuoteNode) = x.value == y.value -function show(io::IO, tv::TypeVar) - if !is(tv.lb, Bottom) - show(io, tv.lb) - print(io, "<:") - end - write(io, tv.name) - if !is(tv.ub, Any) - print(io, "<:") - show(io, tv.ub) - end -end - expand(x) = ccall(:jl_expand, Any, (Any,), x) macroexpand(x) = ccall(:jl_macroexpand, Any, (Any,), x) diff --git a/base/methodshow.jl b/base/methodshow.jl index e57f0521bba84..89d8b5075cd37 100644 --- a/base/methodshow.jl +++ b/base/methodshow.jl @@ -2,7 +2,7 @@ # Method and method-table pretty-printing -function argtype_decl(n, t) # -> (argname, argtype) +function argtype_decl(env, n, t) # -> (argname, argtype) if isa(n,Expr) n = n.args[1] # handle n::T in arg list end @@ -18,12 +18,12 @@ function argtype_decl(n, t) # -> (argname, argtype) if t.parameters[1] === Any return string(s, "..."), "" else - return s, string(t.parameters[1], "...") + return s, string_with_env(env, t.parameters[1]) * "..." end elseif t == ByteString return s, "ByteString" end - return s, string(t) + return s, string_with_env(env, t) end function arg_decl_parts(m::Method) @@ -37,7 +37,8 @@ function arg_decl_parts(m::Method) e = uncompressed_ast(li) argnames = e.args[1] s = symbol("?") - decls = [argtype_decl(get(argnames,i,s), m.sig.parameters[i]) for i=1:length(m.sig.parameters)] + decls = [argtype_decl(:tvar_env => tv, get(argnames,i,s), m.sig.parameters[i]) + for i = 1:length(m.sig.parameters)] return tv, decls, li.file, li.line end @@ -48,8 +49,8 @@ function show(io::IO, m::Method) show_delim_array(io, tv, '{', ',', '}', false) end print(io, "(") - print_joined(io, [isempty(d[2]) ? d[1] : d[1]*"::"*d[2] for d in decls], - ", ", ", ") + decls = [isempty(d[2]) ? d[1] : d[1]*"::"*d[2] for d in decls] + print_joined(io, decls, ", ", ", ") print(io, ")") if line > 0 print(io, " at ", file, ":", line) diff --git a/base/show.jl b/base/show.jl index 1390be925408c..9220b6aa1bdd2 100644 --- a/base/show.jl +++ b/base/show.jl @@ -838,6 +838,26 @@ function ismodulecall(ex::Expr) isa(getfield(current_module(), ex.args[2]), Module) end +function show(io::IO, tv::TypeVar) + tvar_env = isa(io, IOContext) && get(io, :tvar_env, false) + if isa(tvar_env, Vector{Any}) + have_env = true + in_env = (tv in tvar_env::Vector{Any}) + else + have_env = false + in_env = true + end + if !in_env && !is(tv.lb, Bottom) + show(io, tv.lb) + print(io, "<:") + end + write(io, tv.name) + if have_env ? !in_env : !is(tv.ub, Any) + print(io, "<:") + show(io, tv.ub) + end +end + # dump & xdump - structured tree representation like R's str() # - dump is for the user-facing structure # - xdump is for the internal structure From 17341fffb3d083cd4331ea64c5312a99f7942cbc Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Mon, 16 Nov 2015 03:06:32 -0500 Subject: [PATCH 5/6] fix operator precendence printing of (binop)::Expr.typ --- base/interactiveutil.jl | 2 +- base/show.jl | 38 +++++++++++++++++++++++--------------- test/reflection.jl | 8 +++++++- 3 files changed, 31 insertions(+), 17 deletions(-) diff --git a/base/interactiveutil.jl b/base/interactiveutil.jl index b749a15db54a0..b71d8df66e801 100644 --- a/base/interactiveutil.jl +++ b/base/interactiveutil.jl @@ -227,7 +227,7 @@ function code_warntype(io::IO, f, t::ANY) vars = ast.args[2][1] for v in vars print(emph_io, " ", v[1]) - show_expr_type(emph_io, v[2]) + show_expr_type(emph_io, v[2], true) print(emph_io, '\n') end print(emph_io, "\nBody:\n ") diff --git a/base/show.jl b/base/show.jl index 9220b6aa1bdd2..d76ec6d688b59 100644 --- a/base/show.jl +++ b/base/show.jl @@ -363,6 +363,7 @@ isoperator(s::Symbol) = ccall(:jl_is_operator, Cint, (Cstring,), s) != 0 operator_precedence(s::Symbol) = Int(ccall(:jl_operator_precedence, Cint, (Cstring,), s)) operator_precedence(x::Any) = 0 # fallback for generic expression nodes const prec_power = operator_precedence(:(^)) +const prec_decl = operator_precedence(:(::)) is_expr(ex, head::Symbol) = (isa(ex, Expr) && (ex.head == head)) is_expr(ex, head::Symbol, n::Int) = is_expr(ex, head) && length(ex.args) == n @@ -397,19 +398,16 @@ typeemphasize(io::IO) = get(io, :TYPEEMPHASIZE, false) === true const indent_width = 4 -function show_expr_type(io::IO, ty) +function show_expr_type(io::IO, ty, emph) if is(ty, Function) print(io, "::F") elseif is(ty, IntrinsicFunction) print(io, "::I") else - emph = typeemphasize(io) if emph && !isleaftype(ty) emphasize(io, "::$ty") else - if !is(ty, Any) - print(io, "::$ty") - end + print(io, "::$ty") end end end @@ -493,7 +491,10 @@ show_unquoted(io::IO, ex::GlobalRef, ::Int, ::Int) = print(io, ex.mod, '.', function show_unquoted(io::IO, ex::SymbolNode, ::Int, ::Int) print(io, ex.name) - show_expr_type(io, ex.typ) + emphstate = typeemphasize(io) + if emphstate || ex.typ !== Any + show_expr_type(io, ex.typ, emphstate) + end end function show_unquoted(io::IO, ex::QuoteNode, indent::Int, prec::Int) @@ -530,7 +531,17 @@ end # TODO: implement interpolated strings function show_unquoted(io::IO, ex::Expr, indent::Int, prec::Int) head, args, nargs = ex.head, ex.args, length(ex.args) + emphstate = typeemphasize(io) show_type = true + if (ex.head == :(=) || + ex.head == :boundscheck || + ex.head == :gotoifnot || + ex.head == :return) + show_type = false + end + if !emphstate && ex.typ === Any + show_type = false + end # dot (i.e. "x.y") if is(head, :(.)) show_unquoted(io, args[1], indent + indent_width) @@ -581,7 +592,10 @@ function show_unquoted(io::IO, ex::Expr, indent::Int, prec::Int) if (in(ex.args[1], (GlobalRef(Base, :box), TopNode(:box), :throw)) || ismodulecall(ex) || (ex.typ === Any && is_intrinsic_expr(ex.args[1]))) - show_type = typeemphasize(io) + show_type = false + end + if show_type + prec = prec_decl end # scalar multiplication (i.e. "100x") @@ -810,9 +824,9 @@ function show_unquoted(io::IO, ex::Expr, indent::Int, prec::Int) # print anything else as "Expr(head, args...)" else show_type = false - emphstate = typeemphasize(io) if emphstate && ex.head !== :lambda && ex.head !== :method io = IOContext(io, :TYPEEMPHASIZE => false) + emphstate = false end print(io, "\$(Expr(") show(io, ex.head) @@ -822,13 +836,7 @@ function show_unquoted(io::IO, ex::Expr, indent::Int, prec::Int) end print(io, "))") end - if (ex.head == :(=) || - ex.head == :boundscheck || - ex.head == :gotoifnot || - ex.head == :return) - show_type = false - end - show_type && show_expr_type(io, ex.typ) + show_type && show_expr_type(io, ex.typ, emphstate) end function ismodulecall(ex::Expr) diff --git a/test/reflection.jl b/test/reflection.jl index 7cd48d9fcd778..608522b76e29b 100644 --- a/test/reflection.jl +++ b/test/reflection.jl @@ -92,7 +92,7 @@ function funfun(x) x end -tag = Base.have_color ? string("2y",Base.text_colors[:red],"::Any") : "2y::ANY" +tag = Base.have_color ? string("(2y)",Base.text_colors[:red],"::Any") : "(2y)::ANY" @test warntype_hastag(funfun, Tuple{Float64}, tag) # Make sure emphasis is not used for other functions @@ -213,3 +213,9 @@ let t13464 = "hey there sailor" @test startswith(err13464.msg, "expression is not a function call, or is too complex") end end + +let ex = :(a + b) + @test string(ex) == "a + b" + ex.typ = Integer + @test string(ex) == "(a + b)::Integer" +end From 52fecaf2c696160c85c44f389504ae99612ef90f Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Mon, 16 Nov 2015 14:32:27 -0500 Subject: [PATCH 6/6] deprecate tty_size, replace with iosize & export it --- base/Terminals.jl | 36 +++++----------------- base/deprecated.jl | 14 +++++++++ base/dict.jl | 20 +++++++----- base/docs/helpdb/Profile.jl | 7 ++--- base/docs/utils.jl | 12 ++++---- base/env.jl | 13 -------- base/exports.jl | 3 ++ base/interactiveutil.jl | 2 +- base/markdown/render/terminal/render.jl | 4 +-- base/pkg/entry.jl | 2 +- base/profile.jl | 8 ++++- base/range.jl | 13 ++++---- base/show.jl | 41 +++++++++++++------------ base/sparse/sparsematrix.jl | 4 +-- base/sparse/sparsevector.jl | 5 ++- base/stream.jl | 39 +++++++++++++++++++++++ doc/stdlib/io-network.rst | 14 ++++++++- test/dict.jl | 26 ++++++++++------ 18 files changed, 158 insertions(+), 105 deletions(-) diff --git a/base/Terminals.jl b/base/Terminals.jl index 1499c38ce6ef8..384671f065946 100644 --- a/base/Terminals.jl +++ b/base/Terminals.jl @@ -29,7 +29,7 @@ import Base: flush, read, readuntil, - size, + iosize, start_reading, stop_reading, write, @@ -43,7 +43,7 @@ import Base: abstract TextTerminal <: Base.IO # INTERFACE -size(::TextTerminal) = error("Unimplemented") +iosize(::TextTerminal) = error("Unimplemented") writepos(t::TextTerminal, x, y, s::Array{UInt8,1}) = error("Unimplemented") cmove(t::TextTerminal, x, y) = error("Unimplemented") getX(t::TextTerminal) = error("Unimplemented") @@ -88,8 +88,8 @@ function writepos(t::TextTerminal, x, y, args...) cmove(t, x, y) write(t, args...) end -width(t::TextTerminal) = size(t)[2] -height(t::TextTerminal) = size(t)[1] +width(t::TextTerminal) = iosize(t)[2] +height(t::TextTerminal) = iosize(t)[1] # For terminals with buffers flush(t::TextTerminal) = nothing @@ -131,14 +131,10 @@ cmove_line_up(t::UnixTerminal, n) = (cmove_up(t, n); cmove_col(t, 0)) cmove_line_down(t::UnixTerminal, n) = (cmove_down(t, n); cmove_col(t, 0)) cmove_col(t::UnixTerminal, n) = write(t.out_stream, "$(CSI)$(n)G") -@windows_only begin - ispty(s::Base.TTY) = s.ispty - ispty(s) = false -end @windows ? begin function raw!(t::TTYTerminal,raw::Bool) check_open(t.in_stream) - if ispty(t.in_stream) + if Base.ispty(t.in_stream) run(if raw `stty raw -echo onlcr -ocrnl opost` else @@ -162,26 +158,8 @@ disable_bracketed_paste(t::UnixTerminal) = write(t.out_stream, "$(CSI)?2004l") end_keypad_transmit_mode(t::UnixTerminal) = # tput rmkx write(t.out_stream, "$(CSI)?1l\x1b>") -let s = zeros(Int32, 2) - function Base.size(t::TTYTerminal) - @windows_only if ispty(t.out_stream) - try - h,w = map(x->parse(Int,x),split(readall(open(`stty size`, "r", t.out_stream)[1]))) - w > 0 || (w = 80) - h > 0 || (h = 24) - return h,w - catch - return 24,80 - end - end - Base.uv_error("size (TTY)", ccall(:uv_tty_get_winsize, - Int32, (Ptr{Void}, Ptr{Int32}, Ptr{Int32}), - t.out_stream.handle, pointer(s,1), pointer(s,2)) != 0) - w,h = s[1],s[2] - w > 0 || (w = 80) - h > 0 || (h = 24) - (Int(h),Int(w)) - end +function Base.iosize(t::UnixTerminal) + return iosize(t.out_stream) end clear(t::UnixTerminal) = write(t.out_stream, "\x1b[H\x1b[2J") diff --git a/base/deprecated.jl b/base/deprecated.jl index 0187842fb38f1..c55a14c14ac6a 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -904,3 +904,17 @@ end export isreadable, iswritable, isexecutable @deprecate RemoteRef RemoteChannel + +function tty_size() + depwarn("tty_size is deprecated. use `iosize(io)` as a replacement", :tty_size) + if isdefined(Base, :active_repl) + os = REPL.outstream(Base.active_repl) + if isa(os, Terminals.TTYTerminal) + return iosize(os) + end + end + if isdefined(Base, :STDOUT) + return iosize(STDOUT) + end + return iosize() +end diff --git a/base/dict.jl b/base/dict.jl index 09c0bb96a6b5c..2f4c16cdbd261 100644 --- a/base/dict.jl +++ b/base/dict.jl @@ -61,8 +61,7 @@ function _truncate_at_width_or_chars(str, width, chars="", truncmark="…") end showdict(t::Associative; kw...) = showdict(STDOUT, t; kw...) -function showdict{K,V}(io::IO, t::Associative{K,V}; compact = false, - sz=(s = tty_size(); (s[1]-3, s[2]))) +function showdict{K,V}(io::IO, t::Associative{K,V}; compact = false) (:SHOWN_SET => t) in io && (print(io, "#= circular reference =#"); return) recur_io = IOContext(io, :SHOWN_SET => t) @@ -95,11 +94,12 @@ function showdict{K,V}(io::IO, t::Associative{K,V}; compact = false, end # Otherwise show more descriptively, with one line per key/value pair - rows, cols = sz print(io, summary(t)) isempty(t) && return print(io, ":") if limit + sz = iosize(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 " => " @@ -113,6 +113,8 @@ function showdict{K,V}(io::IO, t::Associative{K,V}; compact = false, ks[i] = sprint(0, show, k, env=recur_io) keylen = clamp(length(ks[i]), keylen, div(cols, 3)) end + else + rows = cols = 0 end for (i, (k, v)) in enumerate(t) @@ -149,19 +151,21 @@ summary{T<:Union{KeyIterator,ValueIterator}}(iter::T) = show(io::IO, iter::Union{KeyIterator,ValueIterator}) = show(io, collect(iter)) -showkv(iter::Union{KeyIterator,ValueIterator}; kw...) = showkv(STDOUT, iter; kw...) -function showkv{T<:Union{KeyIterator,ValueIterator}}(io::IO, iter::T; - sz=(s = tty_size(); (s[1]-3, s[2]))) - limit::Bool = limit_output(io) - rows, cols = sz +showkv(iter::Union{KeyIterator,ValueIterator}) = showkv(STDOUT, iter) +function showkv{T<:Union{KeyIterator,ValueIterator}}(io::IO, iter::T) print(io, summary(iter)) isempty(iter) && return print(io, ". ", T<:KeyIterator ? "Keys" : "Values", ":") + limit::Bool = limit_output(io) if limit + sz = iosize(io) + rows, cols = sz[1] - 3, sz[2] rows < 2 && (print(io, " …"); return) cols < 4 && (cols = 4) cols -= 2 # For prefix " " rows -= 2 # For summary and final ⋮ continuation lines + else + rows = cols = 0 end for (i, v) in enumerate(iter) diff --git a/base/docs/helpdb/Profile.jl b/base/docs/helpdb/Profile.jl index 48c2938ed148a..a3a25705d71e7 100644 --- a/base/docs/helpdb/Profile.jl +++ b/base/docs/helpdb/Profile.jl @@ -3,18 +3,17 @@ # Base.Profile """ - print([io::IO = STDOUT,] [data::Vector]; format = :tree, C = false, combine = true, cols = tty_cols()) + print([io::IO = STDOUT,] [data::Vector]; format = :tree, C = false, combine = true) Prints profiling results to `io` (by default, `STDOUT`). If you do not supply a `data` vector, the internal buffer of accumulated backtraces will be used. `format` can be `:tree` or `:flat`. If `C==true`, backtraces from C and Fortran code are shown. `combine==true` -merges instruction pointers that correspond to the same line of code. `cols` controls the -width of the display. +merges instruction pointers that correspond to the same line of code. """ Profile.print(io::IO = STDOUT, data::Vector=?) """ - print([io::IO = STDOUT,] data::Vector, lidict::Dict; format = :tree, combine = true, cols = tty_cols()) + print([io::IO = STDOUT,] data::Vector, lidict::Dict; format = :tree, combine = true) Prints profiling results to `io`. This variant is used to examine results exported by a previous call to [`retrieve`](:func:`retrieve`). Supply the vector `data` of backtraces and diff --git a/base/docs/utils.jl b/base/docs/utils.jl index ed2a3cefac730..6880153db2507 100644 --- a/base/docs/utils.jl +++ b/base/docs/utils.jl @@ -94,7 +94,7 @@ end function repl_search(io::IO, s) pre = "search:" print(io, pre) - printmatches(io, s, completions(s), cols=Base.tty_size()[2]-length(pre)) + printmatches(io, s, completions(s), cols = iosize(io)[2] - length(pre)) println(io, "\n") end @@ -243,7 +243,7 @@ end printmatch(args...) = printfuzzy(STDOUT, args...) -function printmatches(io::IO, word, matches; cols = Base.tty_size()[2]) +function printmatches(io::IO, word, matches; cols = iosize(io)[2]) total = 0 for match in matches total + length(match) + 1 > cols && break @@ -254,9 +254,9 @@ function printmatches(io::IO, word, matches; cols = Base.tty_size()[2]) end end -printmatches(args...; cols = Base.tty_size()[2]) = printmatches(STDOUT, args..., cols = cols) +printmatches(args...; cols = iosize(STDOUT)[2]) = printmatches(STDOUT, args..., cols = cols) -function print_joined_cols(io::IO, ss, delim = "", last = delim; cols = Base.tty_size()[2]) +function print_joined_cols(io::IO, ss, delim = "", last = delim; cols = iosize(io)[2]) i = 0 total = 0 for i = 1:length(ss) @@ -266,13 +266,13 @@ function print_joined_cols(io::IO, ss, delim = "", last = delim; cols = Base.tty print_joined(io, ss[1:i], delim, last) end -print_joined_cols(args...; cols = Base.tty_size()[2]) = print_joined_cols(STDOUT, args...; cols=cols) +print_joined_cols(args...; cols = iosize(STDOUT)[2]) = print_joined_cols(STDOUT, args...; cols=cols) function print_correction(io, word) cors = levsort(word, accessible(current_module())) pre = "Perhaps you meant " print(io, pre) - print_joined_cols(io, cors, ", ", " or "; cols = Base.tty_size()[2]-length(pre)) + print_joined_cols(io, cors, ", ", " or "; cols = iosize(io)[2] - length(pre)) println(io) return end diff --git a/base/env.jl b/base/env.jl index f6f12e80dd0ac..0cf7922660eaf 100644 --- a/base/env.jl +++ b/base/env.jl @@ -165,16 +165,3 @@ function withenv{T<:AbstractString}(f::Function, keyvals::Pair{T}...) end end withenv(f::Function) = f() # handle empty keyvals case; see #10853 - -## misc environment-related functionality ## - -function tty_size() - if isdefined(Base, :active_repl) - os = REPL.outstream(Base.active_repl) - if isa(os, Terminals.TTYTerminal) - return size(os) - end - end - return (parse(Int,get(ENV,"LINES","24")), - parse(Int,get(ENV,"COLUMNS","80"))) -end diff --git a/base/exports.jl b/base/exports.jl index e05c856bd9a3f..88ae15e7f755b 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -1145,9 +1145,12 @@ export getsockname, htol, hton, + IOContext, + iosize, ismarked, isopen, isreadonly, + limit_output, listen, listenany, ltoh, diff --git a/base/interactiveutil.jl b/base/interactiveutil.jl index b71d8df66e801..aff24694ad3ac 100644 --- a/base/interactiveutil.jl +++ b/base/interactiveutil.jl @@ -430,7 +430,7 @@ Print information about exported global variables in a module, optionally restri The memory consumption estimate is an approximate lower bound on the size of the internal structure of the object. """ function whos(io::IO=STDOUT, m::Module=current_module(), pattern::Regex=r"") - maxline = tty_size()[2] + maxline = iosize(io)[2] line = zeros(UInt8, maxline) head = PipeBuffer(maxline + 1) for v in sort!(names(m)) diff --git a/base/markdown/render/terminal/render.jl b/base/markdown/render/terminal/render.jl index 00420ba6a7c23..2f1c495a6ce14 100644 --- a/base/markdown/render/terminal/render.jl +++ b/base/markdown/render/terminal/render.jl @@ -3,7 +3,7 @@ include("formatting.jl") const margin = 2 -cols() = Base.tty_size()[2] +cols(io) = iosize(io)[2] function term(io::IO, content::Vector, cols) isempty(content) && return @@ -14,7 +14,7 @@ function term(io::IO, content::Vector, cols) term(io, content[end], cols) end -term(io::IO, md::MD, columns = cols()) = term(io, md.content, columns) +term(io::IO, md::MD, columns = cols(io)) = term(io, md.content, columns) function term(io::IO, md::Paragraph, columns) print(io, " "^margin) diff --git a/base/pkg/entry.jl b/base/pkg/entry.jl index be2c729fd1b2c..43af5350a6bb7 100644 --- a/base/pkg/entry.jl +++ b/base/pkg/entry.jl @@ -490,7 +490,7 @@ function resolve( end function warnbanner(msg...; label="[ WARNING ]", prefix="") - cols = Base.tty_size()[2] + cols = Base.iosize(STDERR)[2] warn(prefix="", Base.cpad(label,cols,"=")) println(STDERR) warn(prefix=prefix, msg...) diff --git a/base/profile.jl b/base/profile.jl index caaf8be021617..878a58f4f39f9 100644 --- a/base/profile.jl +++ b/base/profile.jl @@ -47,7 +47,13 @@ end clear() = ccall(:jl_profile_clear_data, Void, ()) -function print{T<:Unsigned}(io::IO, data::Vector{T} = fetch(), lidict::Dict = getdict(data); format = :tree, C = false, combine = true, cols = Base.tty_size()[2], maxdepth::Int = typemax(Int), sortedby::Symbol = :filefuncline) +function print{T<:Unsigned}(io::IO, data::Vector{T} = fetch(), lidict::Dict = getdict(data); + format = :tree, + C = false, + combine = true, + maxdepth::Int = typemax(Int), + sortedby::Symbol = :filefuncline) + cols = Base.iosize(io)[2] if format == :tree tree(io, data, lidict, C, combine, cols, maxdepth) elseif format == :flat diff --git a/base/range.jl b/base/range.jl index 396f5d9a307cb..c28aa8c42c714 100644 --- a/base/range.jl +++ b/base/range.jl @@ -253,20 +253,21 @@ It figures out the width in characters of each element, and if they end up too wide, it shows the first and last elements separated by a horizontal elipsis. Typical output will look like `1.0,2.0,3.0,…,4.0,5.0,6.0`. -`print_range(io, r, sz, pre, sep, post, hdots)` uses optional -parameters `sz` for the (rows,cols) of the screen, -`pre` and `post` characters for each printed row, `sep` separator string between -printed elements, `hdots` string for the horizontal ellipsis. +`print_range(io, r, pre, sep, post, hdots)` uses optional +parameters `pre` and `post` characters for each printed row, +`sep` separator string between printed elements, +`hdots` string for the horizontal ellipsis. """ function print_range(io::IO, r::Range, - sz::Tuple{Integer, Integer} = (s = tty_size(); (s[1]-4, s[2])), pre::AbstractString = " ", sep::AbstractString = ",", post::AbstractString = "", hdots::AbstractString = ",\u2026,") # horiz ellipsis # This function borrows from print_matrix() in show.jl # and should be called by writemime (replutil.jl) and by display() - screenheight, screenwidth = sz + limit = limit_output(io) + sz = iosize(io) + screenheight, screenwidth = sz[1] - 4, sz[2] screenwidth -= length(pre) + length(post) postsp = "" sepsize = length(sep) diff --git a/base/show.jl b/base/show.jl index d76ec6d688b59..7a59b6e7c313d 100644 --- a/base/show.jl +++ b/base/show.jl @@ -61,7 +61,13 @@ getindex(io::IO, key) = throw(KeyError(key)) get(io::IOContext, key, default) = get(io.dict, key, default) get(io::IO, key, default) = default -limit_output(io::IO) = get(io, :limit_output, false) === true +" limit_output(io) -> Bool +Output hinting for identifying contexts where the user requested a compact output" +limit_output(::ANY) = false +limit_output(io::IOContext) = get(io, :limit_output, false) === true + +iosize(io::IOContext) = haskey(io, :iosize) ? io[:iosize] : iosize(io.io) + show(io::IO, x::ANY) = show_default(io, x) function show_default(io::IO, x::ANY) @@ -1177,7 +1183,6 @@ 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 = " ", # pre-matrix string sep::AbstractString = " ", # separator between elements post::AbstractString = "", # post-matrix string @@ -1185,7 +1190,12 @@ function print_matrix(io::IO, X::AbstractVecOrMat, vdots::AbstractString = "\u22ee", ddots::AbstractString = " \u22f1 ", hmod::Integer = 5, vmod::Integer = 5) - screenheight, screenwidth = sz + if !limit_output(io) + screenheight = screenwidth = typemax(Int) + else + sz = iosize(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 = "" @@ -1285,7 +1295,8 @@ summary(a::AbstractArray) = string(dims2string(size(a)), " ", typeof(a)) # n-dimensional arrays -function show_nd(io::IO, a::AbstractArray, limit, print_matrix, label_slices) +function show_nd(io::IO, a::AbstractArray, print_matrix, label_slices) + limit::Bool = limit_output(io) if isempty(a) return end @@ -1357,12 +1368,8 @@ end # array output. Not sure I want to do it this way. showarray(X::AbstractArray; kw...) = showarray(STDOUT, X; kw...) function showarray(io::IO, X::AbstractArray; - header::Bool=true, - sz = (s = tty_size(); (s[1]-4, s[2])), - repr=false) - rows, cols = sz + header::Bool=true, repr=false) header && print(io, summary(X)) - limit::Bool = limit_output(io) if !isempty(X) header && println(io, ":") if ndims(X) == 0 @@ -1372,23 +1379,19 @@ function showarray(io::IO, X::AbstractArray; return print(io, undef_ref_str) end end - if !limit - rows = cols = typemax(Int) - sz = (rows, cols) - end if repr - if ndims(X)<=2 + if ndims(X) <= 2 print_matrix_repr(io, X) else - show_nd(io, X, limit, print_matrix_repr, false) + show_nd(io, X, print_matrix_repr, false) end else punct = (" ", " ", "") - if ndims(X)<=2 - print_matrix(io, X, sz, punct...) + if ndims(X) <= 2 + print_matrix(io, X, punct...) else - show_nd(io, X, limit, - (io,slice)->print_matrix(io,slice,sz,punct...), + show_nd(io, X, + (io, slice) -> print_matrix(io, slice, punct...), !repr) end end diff --git a/base/sparse/sparsematrix.jl b/base/sparse/sparsematrix.jl index 6ecb1edf6c3c2..117df81632d31 100644 --- a/base/sparse/sparsematrix.jl +++ b/base/sparse/sparsematrix.jl @@ -77,8 +77,7 @@ convenient iterating over a sparse matrix : nzrange(S::SparseMatrixCSC, col::Integer) = S.colptr[col]:(S.colptr[col+1]-1) function Base.showarray(io::IO, S::SparseMatrixCSC; - header::Bool=true, - rows = Base.tty_size()[1], repr=false) + header::Bool=true, repr=false) # TODO: repr? if header print(io, S.m, "x", S.n, " sparse matrix with ", nnz(S), " ", eltype(S), " entries:") @@ -86,6 +85,7 @@ function Base.showarray(io::IO, S::SparseMatrixCSC; limit::Bool = Base.limit_output(io) if limit + rows = iosize(io)[1] half_screen_rows = div(rows - 8, 2) else half_screen_rows = typemax(Int) diff --git a/base/sparse/sparsevector.jl b/base/sparse/sparsevector.jl index 4d5bdbaacef10..23e1f71afc667 100644 --- a/base/sparse/sparsevector.jl +++ b/base/sparse/sparsevector.jl @@ -585,8 +585,7 @@ getindex(x::AbstractSparseVector, ::Colon) = copy(x) ### show and friends function showarray(io::IO, x::AbstractSparseVector; - header::Bool=true, - rows = Base.tty_size()[1], repr=false) + header::Bool=true, repr=false) n = length(x) nzind = nonzeroinds(x) @@ -598,7 +597,7 @@ function showarray(io::IO, x::AbstractSparseVector; end limit::Bool = Base.limit_output(io) - half_screen_rows = limit ? div(rows - 8, 2) : typemax(Int) + half_screen_rows = limit ? div(iosize(io)[1] - 8, 2) : typemax(Int) pad = ndigits(n) sep = "\n\t" for k = 1:length(nzind) diff --git a/base/stream.jl b/base/stream.jl index b97c9a1892f4a..d8e0ff76b90bb 100644 --- a/base/stream.jl +++ b/base/stream.jl @@ -378,6 +378,45 @@ function close(stream::Union{LibuvStream, LibuvServer}) nothing end +@windows_only begin + ispty(s::TTY) = s.ispty + ispty(s::IO) = false +end + +" iosize(io) -> (lines, columns) +Return the nominal size of the screen that may be used for rendering output to this io object" +iosize(io::IO) = iosize() +iosize() = (parse(Int, get(ENV, "LINES", "24")), + parse(Int, get(ENV, "COLUMNS", "80")))::Tuple{Int, Int} + +function iosize(io::TTY) + local h::Int, w::Int + default_size = iosize() + + @windows_only if ispty(io) + # io is actually a libuv pipe but a cygwin/msys2 pty + try + h, w = map(x -> parse(Int, x), split(readall(open(Base.Cmd(ByteString["stty", "size"]), "r", io)[1]))) + h > 0 || (h = default_size[1]) + w > 0 || (w = default_size[2]) + return h, w + catch + return default_size + end + end + + s1 = Ref{Int32}(0) + s2 = Ref{Int32}(0) + Base.uv_error("size (TTY)", ccall(:uv_tty_get_winsize, + Int32, (Ptr{Void}, Ptr{Int32}, Ptr{Int32}), + io, s1, s2) != 0) + w, h = s1[], s2[] + h > 0 || (h = default_size[1]) + w > 0 || (w = default_size[2]) + return h, w +end + + ### Libuv callbacks ### #from `connect` diff --git a/doc/stdlib/io-network.rst b/doc/stdlib/io-network.rst index 807bfe2b73ab3..627843695079d 100644 --- a/doc/stdlib/io-network.rst +++ b/doc/stdlib/io-network.rst @@ -404,7 +404,7 @@ Text I/O .. Docstring generated from Julia source - Show a more compact representation of a value. This is used for printing array elements. If a new type has a different compact representation, it should overload ``showcompact(io, x)`` where the first argument is a stream. + Show a more compact representation of a value. This is used for printing array elements. If a new type has a different compact representation, it should test ``Base.limit_output(io)`` in its ``show`` method. .. function:: showall(x) @@ -624,6 +624,18 @@ Text I/O Decodes the base64-encoded ``string`` and returns a ``Vector{UInt8}`` of the decoded bytes. +.. function:: iosize(io) -> (lines, columns) + + .. Docstring generated from Julia source + + Return the nominal size of the screen that may be used for rendering output to this io object + +.. function:: limit_output(io) -> Bool + + .. Docstring generated from Julia source + + Output hinting for identifying contexts where the user requested a compact output + Multimedia I/O -------------- diff --git a/test/dict.jl b/test/dict.jl index 3908f42a287c9..5a953b1a8db64 100644 --- a/test/dict.jl +++ b/test/dict.jl @@ -257,7 +257,8 @@ for d in (Dict("\n" => "\n", "1" => "\n", "\n" => "2"), for cols in (12, 40, 80), rows in (2, 10, 24) # Ensure output is limited as requested s = IOBuffer() - Base.showdict(Base.IOContext(s, :limit_output => true), d, sz=(rows, cols)) + io = Base.IOContext(Base.IOContext(s, :limit_output => true), :iosize => (rows, cols)) + Base.showdict(io, d) out = split(takebuf_string(s),'\n') for line in out[2:end] @test strwidth(line) <= cols @@ -266,7 +267,8 @@ for d in (Dict("\n" => "\n", "1" => "\n", "\n" => "2"), for f in (keys, values) s = IOBuffer() - Base.showkv(Base.IOContext(s, :limit_output => true), f(d), sz=(rows, cols)) + io = Base.IOContext(Base.IOContext(s, :limit_output => true), :iosize => (rows, cols)) + Base.showkv(io, f(d)) out = split(takebuf_string(s),'\n') for line in out[2:end] @test strwidth(line) <= cols @@ -284,17 +286,23 @@ end # issue #9463 type Alpha end Base.show(io::IO, ::Alpha) = print(io,"α") -sbuff = IOBuffer() -Base.showdict(Base.IOContext(sbuff, :limit_output => true), Dict(Alpha()=>1), sz=(10,20)) -@test !contains(bytestring(sbuff), "…") +let sbuff = IOBuffer(), + io = Base.IOContext(Base.IOContext(sbuff, :limit_output => true), :iosize => (10, 20)) + + Base.showdict(io, Dict(Alpha()=>1)) + @test !contains(bytestring(sbuff), "…") + @test endswith(bytestring(sbuff), "α => 1") +end # issue #2540 -d = Dict{Any,Any}([x => 1 for x in ['a', 'b', 'c']]) -@test d == Dict('a'=>1, 'b'=>1, 'c'=> 1) +let d = Dict{Any,Any}([x => 1 for x in ['a', 'b', 'c']]) + @test d == Dict('a'=>1, 'b'=>1, 'c'=> 1) +end # issue #2629 -d = Dict{AbstractString,AbstractString}([ a => "foo" for a in ["a","b","c"]]) -@test d == Dict("a"=>"foo","b"=>"foo","c"=>"foo") +let d = Dict{AbstractString,AbstractString}([ a => "foo" for a in ["a","b","c"]]) + @test d == Dict("a"=>"foo","b"=>"foo","c"=>"foo") +end # issue #5886 d5886 = Dict()