# This file is a part of Julia. License is MIT: https://julialang.org/license print(io::IO, s::Symbol) = (write(io,s); nothing) """ IOContext `IOContext` provides a mechanism for passing output configuration settings among [`show`](@ref) methods. In short, it is an immutable dictionary that is a subclass of `IO`. It supports standard dictionary operations such as [`getindex`](@ref), and can also be used as an I/O stream. """ struct IOContext{IO_t <: IO} <: AbstractPipe io::IO_t dict::ImmutableDict{Symbol, Any} function IOContext{IO_t}(io::IO_t, dict::ImmutableDict{Symbol, Any}) where IO_t<:IO assert(!(IO_t <: IOContext)) return new(io, dict) end end """ IOContext(io::IO; properties...) The same as `IOContext(io::IO, KV::Pair)`, but accepting properties as keyword arguments. """ IOContext(io::IO; kws...) = IOContext(convert(IOContext, io); kws...) function IOContext(io::IOContext; kws...) for (k, v) in kws io = IOContext(io, k, v) end return io end convert(::Type{IOContext}, io::IOContext) = io convert(::Type{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::IOContext, key, value) = IOContext(io.io, ImmutableDict{Symbol, Any}(io.dict, key, value)) IOContext(io::IO, key, value) = IOContext(io, ImmutableDict{Symbol, Any}(key, value)) IOContext(io::IO, context::IO) = convert(IOContext, io) """ IOContext(io::IO, context::IOContext) Create an `IOContext` that wraps an alternate `IO` but inherits the properties of `context`. """ IOContext(io::IO, context::IOContext) = IOContext(io, context.dict) """ IOContext(io::IO, KV::Pair) Create an `IOContext` that wraps a given stream, adding the specified `key=>value` pair to the properties of that stream (note that `io` can itself be an `IOContext`). - 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 The following properties are in common use: - `:compact`: Boolean specifying that small values should be printed more compactly, e.g. that numbers should be printed with fewer digits. This is set when printing array elements. - `:limit`: Boolean specifying that containers should be truncated, e.g. showing `…` in place of most elements. - `:displaysize`: A `Tuple{Int,Int}` giving the size in rows and columns to use for text output. This can be used to override the display size for called functions, but to get the size of the screen use the `displaysize` function. ```jldoctest julia> function f(io::IO) if get(io, :short, false) print(io, "short") else print(io, "loooooong") end end f (generic function with 1 method) julia> f(STDOUT) loooooong julia> f(IOContext(STDOUT, :short => true)) short ``` """ 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, ===) 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 displaysize(io::IOContext) = haskey(io, :displaysize) ? io[:displaysize] : displaysize(io.io) show_circular(io::IO, x::ANY) = false function show_circular(io::IOContext, x::ANY) d = 1 for (k, v) in io.dict if k === :SHOWN_SET if v === x print(io, "#= circular reference @-$d =#") return true end d += 1 end end return false end show(io::IO, x::ANY) = show_default(io, x) function show_default(io::IO, x::ANY) t = typeof(x)::DataType show(io, t) print(io, '(') nf = nfields(t) nb = sizeof(x) if nf != 0 || nb == 0 if !show_circular(io, x) recur_io = IOContext(io, :SHOWN_SET => x) for i in 1:nf f = fieldname(t, i) if !isdefined(x, f) print(io, undef_ref_str) else show(recur_io, getfield(x, f)) end if i < nf print(io, ", ") end end end else print(io, "0x") p = data_pointer_from_objref(x) for i in (nb - 1):-1:0 print(io, hex(unsafe_load(convert(Ptr{UInt8}, p + i)), 2)) end end print(io,')') end # Check if a particular symbol is exported from a standard library module function is_exported_from_stdlib(name::Symbol, mod::Module) !isdefined(mod, name) && return false orig = getfield(mod, name) while !(mod === Base || mod === Core) parent = module_parent(mod) if mod === Main || mod === parent || parent === Main return false end mod = parent end return isexported(mod, name) && isdefined(mod, name) && !isdeprecated(mod, name) && getfield(mod, name) === orig end function show(io::IO, f::Function) ft = typeof(f) mt = ft.name.mt if !isdefined(mt, :module) || is_exported_from_stdlib(mt.name, mt.module) || mt.module === Main print(io, mt.name) else print(io, mt.module, ".", mt.name) end end function show(io::IO, x::Core.IntrinsicFunction) name = ccall(:jl_intrinsic_name, Cstring, (Core.IntrinsicFunction,), x) print(io, unsafe_string(name)) end show(io::IO, ::Core.TypeofBottom) = print(io, "Union{}") function show(io::IO, x::Union) print(io, "Union") sorted_types = sort!(uniontypes(x); by=string) show_comma_array(io, sorted_types, '{', '}') end function print_without_params(x::ANY) if isa(x,UnionAll) b = unwrap_unionall(x) return isa(b,DataType) && b.name.wrapper === x end return false end function show(io::IO, x::UnionAll) if print_without_params(x) return show(io, unwrap_unionall(x).name) end show(IOContext(io, :unionall_env => x.var), x.body) print(io, " where ") show(io, x.var) end show(io::IO, x::DataType) = show_datatype(io, x) function show_datatype(io::IO, x::DataType) istuple = x.name === Tuple.name if (!isempty(x.parameters) || istuple) && x !== Tuple n = length(x.parameters) # Print homogeneous tuples with more than 3 elements compactly as NTuple{N, T} if istuple && n > 3 && all(i -> (x.parameters[1] === i), x.parameters) print(io, "NTuple{", n, ',', x.parameters[1], "}") else show(io, x.name) # Do not print the type parameters for the primary type if we are # printing a method signature or type parameter. # Always print the type parameter if we are printing the type directly # since this information is still useful. print(io, '{') for (i, p) in enumerate(x.parameters) show(io, p) i < n && print(io, ',') end print(io, '}') end else show(io, x.name) end end macro show(exs...) blk = Expr(:block) for ex in exs push!(blk.args, :(println($(sprint(show_unquoted,ex)*" = "), repr(begin value=$(esc(ex)) end)))) end if !isempty(exs); push!(blk.args, :value); end return blk end function show(io::IO, tn::TypeName) if is_exported_from_stdlib(tn.name, tn.module) || tn.module === Main print(io, tn.name) else print(io, tn.module, '.', tn.name) end end show(io::IO, ::Void) = print(io, "nothing") show(io::IO, b::Bool) = print(io, b ? "true" : "false") show(io::IO, n::Signed) = (write(io, dec(n)); nothing) show(io::IO, n::Unsigned) = print(io, "0x", hex(n,sizeof(n)<<1)) print(io::IO, n::Unsigned) = print(io, dec(n)) show(io::IO, p::Ptr) = print(io, typeof(p), " @0x$(hex(UInt(p), Sys.WORD_SIZE>>2))") function show(io::IO, p::Pair) if typeof(p.first) != typeof(p).parameters[1] || typeof(p.second) != typeof(p).parameters[2] return show_default(io, p) end isa(p.first,Pair) && print(io, "(") show(io, p.first) isa(p.first,Pair) && print(io, ")") print(io, "=>") isa(p.second,Pair) && print(io, "(") show(io, p.second) isa(p.second,Pair) && print(io, ")") nothing end function show(io::IO, m::Module) if m === Main print(io, "Main") else print(io, join(fullname(m),".")) end end function sourceinfo_slotnames(src::CodeInfo) slotnames = src.slotnames isa(slotnames, Array) || return String[] names = Dict{String,Int}() printnames = Vector{String}(length(slotnames)) for i in eachindex(slotnames) name = string(slotnames[i]) idx = get!(names, name, i) if idx != i printname = "$name@_$i" idx > 0 && (printnames[idx] = "$name@_$idx") names[name] = 0 else printname = name end printnames[i] = printname end return printnames end function show(io::IO, l::Core.MethodInstance) if isdefined(l, :def) if l.def.isstaged && l === l.def.generator print(io, "MethodInstance generator for ") show(io, l.def) else print(io, "MethodInstance for ") show_tuple_as_call(io, l.def.name, l.specTypes) end else print(io, "Toplevel MethodInstance thunk") end end function show(io::IO, src::CodeInfo) # Fix slot names and types in function body print(io, "CodeInfo(") lambda_io = IOContext(io, :SOURCEINFO => src) if src.slotnames !== nothing lambda_io = IOContext(lambda_io, :SOURCE_SLOTNAMES => sourceinfo_slotnames(src)) end body = Expr(:body) body.args = src.code show(lambda_io, body) print(io, ")") end function show_delim_array(io::IO, itr::Union{AbstractArray,SimpleVector}, op, delim, cl, delim_one, i1=first(linearindices(itr)), l=last(linearindices(itr))) print(io, op) if !show_circular(io, itr) recur_io = IOContext(io, :SHOWN_SET => itr) if !haskey(io, :compact) recur_io = IOContext(recur_io, :compact => true) end first = true i = i1 if l >= i1 while true if !isassigned(itr, i) print(io, undef_ref_str) else x = itr[i] show(recur_io, x) end i += 1 if i > l delim_one && first && print(io, delim) break end first = false print(io, delim) print(io, ' ') end end end print(io, cl) end function show_delim_array(io::IO, itr, op, delim, cl, delim_one, i1=1, n=typemax(Int)) print(io, op) if !show_circular(io, itr) recur_io = IOContext(io, :SHOWN_SET => itr) state = start(itr) first = true while i1 > 1 && !done(itr, state) _, state = next(itr, state) i1 -= 1 end if !done(itr, state) while true x, state = next(itr, state) show(recur_io, x) i1 += 1 if done(itr, state) || i1 > n delim_one && first && print(io, delim) break end first = false print(io, delim) print(io, ' ') end end end print(io, cl) end show_comma_array(io::IO, itr, o, c) = show_delim_array(io, itr, o, ',', c, false) show(io::IO, t::Tuple) = show_delim_array(io, t, '(', ',', ')', true) show(io::IO, v::SimpleVector) = show_delim_array(io, v, "svec(", ',', ')', false) show(io::IO, s::Symbol) = show_unquoted_quote_expr(io, s, 0, 0) ## Abstract Syntax Tree (AST) printing ## # Summary: # print(io, ex) defers to show_unquoted(io, ex) # show(io, ex) defers to show_unquoted(io, QuoteNode(ex)) # show_unquoted(io, ex) does the heavy lifting # # AST printing should follow two rules: # 1. parse(string(ex)) == ex # 2. eval(parse(repr(ex))) == ex # # Rule 1 means that printing an expression should generate Julia code which # could be reparsed to obtain the original expression. This code should be # unambiguous and as readable as possible. # # Rule 2 means that showing an expression should generate a quoted version of # print’s output. Parsing and then evaling this output should return the # original expression. # # This is consistent with many other show methods, i.e.: # show(Set([1,2,3])) # ==> "Set{Int64}([2,3,1])" # eval(parse("Set{Int64}([2,3,1])”) # ==> An actual set # While this isn’t true of ALL show methods, it is of all ASTs. const ExprNode = Union{Expr, QuoteNode, Slot, LineNumberNode, LabelNode, GotoNode, GlobalRef} # Operators have precedence levels from 1-N, and show_unquoted defaults to a # precedence level of 0 (the fourth argument). The top-level print and show # methods use a precedence of -1 to specially allow space-separated macro syntax print( io::IO, ex::ExprNode) = (show_unquoted(io, ex, 0, -1); nothing) show( io::IO, ex::ExprNode) = show_unquoted_quote_expr(io, ex, 0, -1) show_unquoted(io::IO, ex) = show_unquoted(io, ex, 0, 0) show_unquoted(io::IO, ex, indent::Int) = show_unquoted(io, ex, indent, 0) show_unquoted(io::IO, ex, ::Int,::Int) = show(io, ex) ## AST printing constants ## const indent_width = 4 const quoted_syms = Set{Symbol}([:(:),:(::),:(:=),:(=),:(==),:(!=),:(===),:(!==),:(=>),:(>=),:(<=)]) const uni_ops = Set{Symbol}([:(+), :(-), :(!), :(¬), :(~), :(<:), :(>:), :(√), :(∛), :(∜)]) const expr_infix_wide = Set{Symbol}([ :(=), :(+=), :(-=), :(*=), :(/=), :(\=), :(^=), :(&=), :(|=), :(÷=), :(%=), :(>>>=), :(>>=), :(<<=), :(.=), :(.+=), :(.-=), :(.*=), :(./=), :(.\=), :(.^=), :(.&=), :(.|=), :(.÷=), :(.%=), :(.>>>=), :(.>>=), :(.<<=), :(&&), :(||), :(<:), :($=), :(⊻=)]) # `$=` should be removed after deprecation is removed, issue #18977 const expr_infix = Set{Symbol}([:(:), :(->), Symbol("::")]) const expr_infix_any = union(expr_infix, expr_infix_wide) const all_ops = union(quoted_syms, uni_ops, expr_infix_any) const expr_calls = Dict(:call => ('(',')'), :calldecl => ('(',')'), :ref => ('[',']'), :curly => ('{','}'), :(.) => ('(',')')) const expr_parens = Dict(:tuple=>('(',')'), :vcat=>('[',']'), :hcat =>('[',']'), :row =>('[',']'), :vect=>('[',']')) ## AST decoding helpers ## is_id_start_char(c::Char) = ccall(:jl_id_start_char, Cint, (UInt32,), c) != 0 is_id_char(c::Char) = ccall(:jl_id_char, Cint, (UInt32,), c) != 0 function isidentifier(s::AbstractString) i = start(s) done(s, i) && return false (c, i) = next(s, i) is_id_start_char(c) || return false while !done(s, i) (c, i) = next(s, i) is_id_char(c) || return false end return true end isidentifier(s::Symbol) = isidentifier(string(s)) isoperator(s::Symbol) = ccall(:jl_is_operator, Cint, (Cstring,), s) != 0 """ operator_precedence(s::Symbol) Return an integer representing the precedence of operator `s`, relative to other operators. Higher-numbered operators take precedence over lower-numbered operators. Return `0` if `s` is not a valid operator. # Examples ```jldoctest julia> Base.operator_precedence(:+), Base.operator_precedence(:*), Base.operator_precedence(:.) (9,11,15) julia> Base.operator_precedence(:+=), Base.operator_precedence(:(=)) # (Note the necessary parens on `:(=)`) (1,1) ``` """ 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 is_linenumber(ex::LineNumberNode) = true is_linenumber(ex::Expr) = (ex.head == :line) is_linenumber(ex) = false is_quoted(ex) = false is_quoted(ex::QuoteNode) = true is_quoted(ex::Expr) = is_expr(ex, :quote, 1) || is_expr(ex, :inert, 1) unquoted(ex::QuoteNode) = ex.value unquoted(ex::Expr) = ex.args[1] ## AST printing helpers ## typeemphasize(io::IO) = get(io, :TYPEEMPHASIZE, false) === true const indent_width = 4 function show_expr_type(io::IO, ty::ANY, emph::Bool) if ty === Function print(io, "::F") elseif ty === Core.IntrinsicFunction print(io, "::I") else if emph && (!isleaftype(ty) || ty == Core.Box) emphasize(io, "::$ty") else print(io, "::$ty") end end end emphasize(io, str::AbstractString) = have_color ? print_with_color(Base.error_color(), io, str; bold = true) : print(io, uppercase(str)) show_linenumber(io::IO, line) = print(io," # line ",line,':') show_linenumber(io::IO, line, file) = print(io," # ", file,", line ",line,':') # show a block, e g if/for/etc function show_block(io::IO, head, args::Vector, body, indent::Int) print(io, head, ' ') show_list(io, args, ", ", indent) ind = head === :module || head === :baremodule ? indent : indent + indent_width exs = (is_expr(body, :block) || is_expr(body, :body)) ? body.args : Any[body] for ex in exs if !is_linenumber(ex); print(io, '\n', " "^ind); end show_unquoted(io, ex, ind, -1) end print(io, '\n', " "^indent) end show_block(io::IO,head, block,i::Int) = show_block(io,head, [], block,i) function show_block(io::IO, head, arg, block, i::Int) if is_expr(arg, :block) show_block(io, head, arg.args, block, i) else show_block(io, head, Any[arg], block, i) end end # show an indented list function show_list(io::IO, items, sep, indent::Int, prec::Int=0, enclose_operators::Bool=false) n = length(items) if n == 0; return end indent += indent_width first = true for item in items !first && print(io, sep) parens = enclose_operators && isa(item,Symbol) && isoperator(item) parens && print(io, '(') show_unquoted(io, item, indent, prec) parens && print(io, ')') first = false end end # show an indented list inside the parens (op, cl) function show_enclosed_list(io::IO, op, items, sep, cl, indent, prec=0, encl_ops=false) print(io, op) show_list(io, items, sep, indent, prec, encl_ops) print(io, cl) end # show a normal (non-operator) function call, e.g. f(x, y) or A[z] function show_call(io::IO, head, func, func_args, indent) op, cl = expr_calls[head] if isa(func, Symbol) || (isa(func, Expr) && (func.head == :. || func.head == :curly)) show_unquoted(io, func, indent) else print(io, '(') show_unquoted(io, func, indent) print(io, ')') end if head == :(.) print(io, '.') end if !isempty(func_args) && isa(func_args[1], Expr) && func_args[1].head === :parameters print(io, op) show_list(io, func_args[2:end], ", ", indent) print(io, "; ") show_list(io, func_args[1].args, ", ", indent) print(io, cl) else show_enclosed_list(io, op, func_args, ", ", cl, indent) end end ## AST printing ## show_unquoted(io::IO, sym::Symbol, ::Int, ::Int) = print(io, sym) show_unquoted(io::IO, ex::LineNumberNode, ::Int, ::Int) = show_linenumber(io, ex.line) show_unquoted(io::IO, ex::LabelNode, ::Int, ::Int) = print(io, ex.label, ": ") show_unquoted(io::IO, ex::GotoNode, ::Int, ::Int) = print(io, "goto ", ex.label) show_unquoted(io::IO, ex::GlobalRef, ::Int, ::Int) = print(io, ex.mod, '.', ex.name) function show_unquoted(io::IO, ex::Slot, ::Int, ::Int) typ = isa(ex,TypedSlot) ? ex.typ : Any slotid = ex.id src = get(io, :SOURCEINFO, false) if isa(src, CodeInfo) slottypes = (src::CodeInfo).slottypes if isa(slottypes, Array) && slotid <= length(slottypes::Array) slottype = slottypes[slotid] # The Slot in assignment can somehow have an Any type if isa(slottype, Type) && isa(typ, Type) && slottype <: typ typ = slottype end end end slotnames = get(io, :SOURCE_SLOTNAMES, false) if (isa(slotnames, Vector{String}) && slotid <= length(slotnames::Vector{String})) print(io, (slotnames::Vector{String})[slotid]) else print(io, "_", slotid) end emphstate = typeemphasize(io) if emphstate || (typ !== Any && isa(ex,TypedSlot)) show_expr_type(io, typ, emphstate) end end function show_unquoted(io::IO, ex::QuoteNode, indent::Int, prec::Int) if isa(ex.value, Symbol) show_unquoted_quote_expr(io, ex.value, indent, prec) else print(io, "\$(QuoteNode(") show(io, ex.value) print(io, "))") end end function show_unquoted_quote_expr(io::IO, value, indent::Int, prec::Int) if isa(value, Symbol) && !(value in quoted_syms) s = string(value) if isidentifier(s) || isoperator(value) print(io, ":") print(io, value) else print(io, "Symbol(\"", escape_string(s), "\")") end else if isa(value,Expr) && value.head === :block show_block(io, "quote", value, indent) print(io, "end") else print(io, ":(") show_unquoted(io, value, indent+indent_width, -1) print(io, ")") end end end function show_generator(io, ex, indent) if ex.head === :flatten fg = ex ranges = Any[] while isa(fg, Expr) && fg.head === :flatten push!(ranges, fg.args[1].args[2:end]) fg = fg.args[1].args[1] end push!(ranges, fg.args[2:end]) show_unquoted(io, fg.args[1], indent) for r in ranges print(io, " for ") show_list(io, r, ", ", indent) end else show_unquoted(io, ex.args[1], indent) print(io, " for ") show_list(io, ex.args[2:end], ", ", indent) end 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 == :line || 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"), but not compact broadcast exps if head === :(.) && !is_expr(args[2], :tuple) show_unquoted(io, args[1], indent + indent_width) print(io, '.') if is_quoted(args[2]) show_unquoted(io, unquoted(args[2]), indent + indent_width) else print(io, '(') show_unquoted(io, args[2], indent + indent_width) print(io, ')') end # infix (i.e. "x <: y" or "x = y") elseif (head in expr_infix_any && nargs==2) || (head === :(:) && nargs==3) func_prec = operator_precedence(head) head_ = head in expr_infix_wide ? " $head " : head if func_prec <= prec show_enclosed_list(io, '(', args, head_, ')', indent, func_prec, true) else show_list(io, args, head_, indent, func_prec, true) end # list (i.e. "(1, 2, 3)" or "[1, 2, 3]") elseif haskey(expr_parens, head) # :tuple/:vcat op, cl = expr_parens[head] if head === :vcat sep = "; " elseif head === :hcat || head === :row sep = " " else sep = ", " end head !== :row && print(io, op) show_list(io, args, sep, indent) if nargs == 1 if head === :tuple print(io, ',') elseif head === :vcat print(io, ';') end end head !== :row && print(io, cl) # function call elseif head === :call && nargs >= 1 func = args[1] fname = isa(func,GlobalRef) ? func.name : func func_prec = operator_precedence(fname) if func_prec > 0 || fname in uni_ops func = fname end func_args = args[2:end] if (in(ex.args[1], (GlobalRef(Base, :bitcast), :throw)) || ismodulecall(ex)) show_type = false end if show_type prec = prec_decl end # scalar multiplication (i.e. "100x") if (func === :* && length(func_args)==2 && isa(func_args[1], Real) && isa(func_args[2], Symbol)) if func_prec <= prec show_enclosed_list(io, '(', func_args, "", ')', indent, func_prec) else show_list(io, func_args, "", indent, func_prec) end # unary operator (i.e. "!z") elseif isa(func,Symbol) && func in uni_ops && length(func_args) == 1 show_unquoted(io, func, indent) if isa(func_args[1], Expr) || func_args[1] in all_ops show_enclosed_list(io, '(', func_args, ", ", ')', indent, func_prec) else show_unquoted(io, func_args[1]) end # binary operator (i.e. "x + y") elseif func_prec > 0 # is a binary operator na = length(func_args) if (na == 2 || (na > 2 && func in (:+, :++, :*))) && all(!isa(a, Expr) || a.head !== :... for a in func_args) sep = " $func " if func_prec <= prec show_enclosed_list(io, '(', func_args, sep, ')', indent, func_prec, true) else show_list(io, func_args, sep, indent, func_prec, true) end elseif na == 1 # 1-argument call to normally-binary operator op, cl = expr_calls[head] print(io, "(") show_unquoted(io, func, indent) print(io, ")") show_enclosed_list(io, op, func_args, ", ", cl, indent) else show_call(io, head, func, func_args, indent) end # normal function (i.e. "f(x,y)") else show_call(io, head, func, func_args, indent) end # other call-like expressions ("A[1,2]", "T{X,Y}", "f.(X,Y)") elseif haskey(expr_calls, head) && nargs >= 1 # :ref/:curly/:calldecl/:(.) funcargslike = head == :(.) ? ex.args[2].args : ex.args[2:end] show_call(io, head, ex.args[1], funcargslike, indent) # comprehensions elseif head === :typed_comprehension && length(args) == 2 show_unquoted(io, args[1], indent) print(io, '[') show_generator(io, args[2], indent) print(io, ']') elseif head === :comprehension && length(args) == 1 print(io, '[') show_generator(io, args[1], indent) print(io, ']') elseif (head === :generator && length(args) >= 2) || (head === :flatten && length(args) == 1) print(io, '(') show_generator(io, ex, indent) print(io, ')') elseif head === :filter && length(args) == 2 show_unquoted(io, args[2], indent) print(io, " if ") show_unquoted(io, args[1], indent) # comparison (i.e. "x < y < z") elseif head === :comparison && nargs >= 3 && (nargs&1==1) comp_prec = minimum(operator_precedence, args[2:2:end]) if comp_prec <= prec show_enclosed_list(io, '(', args, " ", ')', indent, comp_prec) else show_list(io, args, " ", indent, comp_prec) end # function calls need to transform the function from :call to :calldecl # so that operators are printed correctly elseif head === :function && nargs==2 && is_expr(args[1], :call) show_block(io, head, Expr(:calldecl, args[1].args...), args[2], indent) print(io, "end") elseif head === :function && nargs == 1 print(io, "function ", args[1], " end") # block with argument elseif head in (:for,:while,:function,:if) && nargs==2 show_block(io, head, args[1], args[2], indent) print(io, "end") elseif head === :module && nargs==3 && isa(args[1],Bool) show_block(io, args[1] ? :module : :baremodule, args[2], args[3], indent) print(io, "end") # type declaration elseif head === :type && nargs==3 show_block(io, args[1] ? Symbol("mutable struct") : Symbol("struct"), args[2], args[3], indent) print(io, "end") elseif head === :bitstype && nargs == 2 print(io, "primitive type ") show_list(io, reverse(args), ' ', indent) print(io, " end") elseif head === :abstract && nargs == 1 print(io, "abstract type ") show_list(io, args, ' ', indent) print(io, " end") # empty return (i.e. "function f() return end") elseif head === :return && nargs == 1 && args[1] === nothing print(io, head) # type annotation (i.e. "::Int") elseif head === Symbol("::") && nargs == 1 print(io, "::") show_unquoted(io, args[1], indent) # var-arg declaration or expansion # (i.e. "function f(L...) end" or "f(B...)") elseif head === :(...) && nargs == 1 show_unquoted(io, args[1], indent) print(io, "...") elseif (nargs == 0 && head in (:break, :continue)) print(io, head) elseif (nargs == 1 && head in (:return, :const)) || head in (:local, :global, :export) print(io, head, ' ') show_list(io, args, ", ", indent) elseif head === :macrocall && nargs >= 1 # Use the functional syntax unless specifically designated with prec=-1 if prec >= 0 show_call(io, :call, ex.args[1], ex.args[2:end], indent) else show_list(io, args, ' ', indent) end elseif head === :line && 1 <= nargs <= 2 show_linenumber(io, args...) elseif head === :if && nargs == 3 # if/else show_block(io, "if", args[1], args[2], indent) show_block(io, "else", args[3], indent) print(io, "end") elseif head === :try && 3 <= nargs <= 4 show_block(io, "try", args[1], indent) if is_expr(args[3], :block) show_block(io, "catch", args[2] === false ? Any[] : args[2], args[3], indent) end if nargs >= 4 && is_expr(args[4], :block) show_block(io, "finally", Any[], args[4], indent) end print(io, "end") elseif head === :let && nargs >= 1 show_block(io, "let", args[2:end], args[1], indent); print(io, "end") elseif head === :block || head === :body show_block(io, "begin", ex, indent); print(io, "end") elseif head === :quote && nargs == 1 && isa(args[1],Symbol) show_unquoted_quote_expr(io, args[1], indent, 0) elseif head === :gotoifnot && nargs == 2 print(io, "unless ") show_list(io, args, " goto ", indent) elseif head === :string && nargs == 1 && isa(args[1], AbstractString) show(io, args[1]) elseif head === :null print(io, "nothing") elseif head === :kw && length(args)==2 show_unquoted(io, args[1], indent+indent_width) print(io, '=') show_unquoted(io, args[2], indent+indent_width) elseif head === :string print(io, '"') for x in args if !isa(x,AbstractString) print(io, "\$(") if isa(x,Symbol) && !(x in quoted_syms) print(io, x) else show_unquoted(io, x) end print(io, ")") else escape_string(io, x, "\"\$") end end print(io, '"') elseif (head === :&#= || head === :$=#) && length(args) == 1 print(io, head) a1 = args[1] parens = (isa(a1,Expr) && a1.head !== :tuple) || (isa(a1,Symbol) && isoperator(a1)) parens && print(io, "(") show_unquoted(io, a1) parens && print(io, ")") # transpose elseif (head === Symbol('\'') || head === Symbol(".'")) && length(args) == 1 if isa(args[1], Symbol) show_unquoted(io, args[1]) else print(io, "(") show_unquoted(io, args[1]) print(io, ")") end print(io, head) # `where` syntax elseif head === :where && length(args) > 1 parens = 1 <= prec parens && print(io, "(") show_unquoted(io, args[1], indent, operator_precedence(:(::))) print(io, " where ") if nargs == 2 show_unquoted(io, args[2], indent, 1) else print(io, "{") show_list(io, args[2:end], ", ", indent) print(io, "}") end parens && print(io, ")") elseif head === :import || head === :importall || head === :using print(io, head) first = true for a = args if first print(io, ' ') first = false else print(io, '.') end if a !== :. print(io, a) end end elseif head === :meta && length(args) >= 2 && args[1] === :push_loc print(io, "# meta: location ", join(args[2:end], " ")) show_type = false elseif head === :meta && length(args) == 1 && args[1] === :pop_loc print(io, "# meta: pop location") show_type = false # print anything else as "Expr(head, args...)" else show_type = false if emphstate && ex.head !== :lambda && ex.head !== :method io = IOContext(io, :TYPEEMPHASIZE => false) emphstate = false end print(io, "\$(Expr(") show(io, ex.head) for arg in args print(io, ", ") show(io, arg) end print(io, "))") end show_type && show_expr_type(io, ex.typ, emphstate) nothing end function show_tuple_as_call(io::IO, name::Symbol, sig::Type) # print a method signature tuple for a lambda definition color = have_color && get(io, :backtrace, false) ? stackframe_function_color() : :nothing if sig === Tuple Base.print_with_color(color, io, name, "(...)") return end sig = unwrap_unionall(sig).parameters Base.with_output_color(color, io) do io ft = sig[1] uw = unwrap_unionall(ft) if ft <: Function && isa(uw,DataType) && isempty(uw.parameters) && isdefined(uw.name.module, uw.name.mt.name) && ft == typeof(getfield(uw.name.module, uw.name.mt.name)) print(io, uw.name.mt.name) elseif isa(ft, DataType) && ft.name === Type.body.name && isleaftype(ft) f = ft.parameters[1] print(io, f) else print(io, "(::", ft, ")") end end first = true print_style = have_color && get(io, :backtrace, false) ? :bold : :nothing print_with_color(print_style, io, "(") for i = 2:length(sig) # fixme (iter): `eachindex` with offset? first || print(io, ", ") first = false print(io, "::", sig[i]) end print_with_color(print_style, io, ")") nothing end function ismodulecall(ex::Expr) return ex.head == :call && (ex.args[1] === GlobalRef(Base,:getfield) || ex.args[1] === GlobalRef(Core,:getfield)) && isa(ex.args[2], Symbol) && isdefined(current_module(), ex.args[2]) && isa(getfield(current_module(), ex.args[2]), Module) end function show(io::IO, tv::TypeVar) # If we are in the `unionall_env`, the type-variable is bound # and the type constraints are already printed. # We don't need to print it again. # Otherwise, the lower bound should be printed if it is not `Bottom` # and the upper bound should be printed if it is not `Any`. in_env = (:unionall_env => tv) in io function show_bound(io::IO, b::ANY) parens = isa(b,UnionAll) && !print_without_params(b) parens && print(io, "(") show(io, b) parens && print(io, ")") end lb, ub = tv.lb, tv.ub if !in_env && lb !== Bottom if ub === Any write(io, tv.name) print(io, ">:") show_bound(io, lb) else show_bound(io, lb) print(io, "<:") write(io, tv.name) end else write(io, tv.name) end if !in_env && ub !== Any print(io, "<:") show_bound(io, ub) end nothing end function dump(io::IO, x::SimpleVector, n::Int, indent) if isempty(x) print(io, "empty SimpleVector") return end print(io, "SimpleVector") if n > 0 for i = 1:length(x) println(io) print(io, indent, " ", i, ": ") if isassigned(x,i) dump(io, x[i], n - 1, string(indent, " ")) else print(io, undef_ref_str) end end end nothing end function dump(io::IO, x::ANY, n::Int, indent) T = typeof(x) if isa(x, Function) print(io, x, " (function of type ", T, ")") else print(io, T) end if nfields(T) > 0 if n > 0 for field in (isa(x,Tuple) ? (1:length(x)) : fieldnames(T)) println(io) print(io, indent, " ", field, ": ") if isdefined(x,field) dump(io, getfield(x, field), n - 1, string(indent, " ")) else print(io, undef_ref_str) end end end else !isa(x,Function) && print(io, " ", x) end nothing end dump(io::IO, x::Module, n::Int, indent) = print(io, "Module ", x) dump(io::IO, x::String, n::Int, indent) = (print(io, "String "); show(io, x)) dump(io::IO, x::Symbol, n::Int, indent) = print(io, typeof(x), " ", x) dump(io::IO, x::Union, n::Int, indent) = print(io, x) function dump_elts(io::IO, x::Array, n::Int, indent, i0, i1) for i in i0:i1 print(io, indent, " ", i, ": ") if !isassigned(x,i) print(io, undef_ref_str) else dump(io, x[i], n - 1, string(indent, " ")) end i < i1 && println(io) end end function dump(io::IO, x::Array, n::Int, indent) print(io, "Array{$(eltype(x))}($(size(x)))") if eltype(x) <: Number print(io, " ") show(io, x) else if n > 0 && !isempty(x) println(io) if get(io, :limit, false) dump_elts(io, x, n, indent, 1, (length(x) <= 10 ? length(x) : 5)) if length(x) > 10 println(io) println(io, indent, " ...") dump_elts(io, x, n, indent, length(x)-4, length(x)) end else dump_elts(io, x, n, indent, 1, length(x)) end end end nothing end # Types function dump(io::IO, x::DataType, n::Int, indent) print(io, x) if x !== Any print(io, " <: ", supertype(x)) end if n > 0 && !(x <: Tuple) tvar_io::IOContext = io for tparam in x.parameters # approximately recapture the list of tvar parameterization # that may be used by the internal fields if isa(tparam, TypeVar) tvar_io = IOContext(tvar_io, :unionall_env => tparam) end end fields = fieldnames(x) fieldtypes = x.types for idx in 1:length(fields) println(io) print(io, indent, " ", fields[idx], "::") print(tvar_io, fieldtypes[idx]) end end nothing end # dumptype is for displaying abstract type hierarchies, # based on Jameson Nash's examples/typetree.jl function dumptype(io::IO, x::ANY, n::Int, indent) print(io, x) n == 0 && return # too deeply nested isa(x, DataType) && x.abstract && dumpsubtypes(io, x, Main, n, indent) nothing end directsubtype(a::DataType, b::DataType) = supertype(a).name === b.name directsubtype(a::UnionAll, b::DataType) = directsubtype(a.body, b) directsubtype(a::Union, b::DataType) = directsubtype(a.a, b) || directsubtype(a.b, b) # Fallback to handle TypeVar's directsubtype(a, b::DataType) = false function dumpsubtypes(io::IO, x::DataType, m::Module, n::Int, indent) for s in names(m, true) if isdefined(m, s) && !isdeprecated(m, s) t = getfield(m, s) if t === x || t === m continue elseif isa(t, Module) && module_name(t) === s && module_parent(t) === m # recurse into primary module bindings dumpsubtypes(io, x, t, n, indent) elseif isa(t, UnionAll) && directsubtype(t::UnionAll, x) dt = unwrap_unionall(t) println(io) if isa(dt, DataType) && dt.name.wrapper === t # primary type binding print(io, indent, " ") dumptype(io, dt, n - 1, string(indent, " ")) else # aliases to types print(io, indent, " ", m, ".", s, "{") tvar_io::IOContext = io tp = t while true show(tvar_io, tp.var) tvar_io = IOContext(tvar_io, :unionall_env, tp.var) tp = tp.body if isa(tp, UnionAll) print(io, ", ") else print(io, "} = ") break end end show(tvar_io, tp) end elseif isa(t, Union) && directsubtype(t::Union, x) println(io) print(io, indent, " ", m, ".", s, " = ", t) elseif isa(t, DataType) && directsubtype(t::DataType, x) println(io) if t.name.module !== m || t.name.name != s # aliases to types print(io, indent, " ", m, ".", s, " = ") show(io, t) else # primary type binding print(io, indent, " ") dumptype(io, t, n - 1, string(indent, " ")) end end end end nothing end # For abstract types, use _dumptype only if it's a form that will be called # interactively. dump(io::IO, x::DataType; maxdepth=8) = ((x.abstract ? dumptype : dump)(io, x, maxdepth, ""); println(io)) dump(io::IO, arg; maxdepth=8) = (dump(io, arg, maxdepth, ""); println(io)) dump(arg; maxdepth=8) = dump(IOContext(STDOUT::IO, :limit => true), arg; maxdepth=maxdepth) """ `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(io::IO, x::Any) = (0, length(sprint(0, show, x, env=io))) alignment(io::IO, x::Number) = (length(sprint(0, show, x, env=io)), 0) "`alignment(42)` yields (2,0)" alignment(io::IO, x::Integer) = (length(sprint(0, show, x, env=io)), 0) "`alignment(4.23)` yields (1,3) for `4` and `.23`" function alignment(io::IO, x::Real) m = match(r"^(.*?)((?:[\.eE].*)?)$", sprint(0, show, x, env=io)) m === nothing ? (length(sprint(0, show, 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(io::IO, x::Complex) m = match(r"^(.*[^e][\+\-])(.*)$", sprint(0, show, x, env=io)) m === nothing ? (length(sprint(0, show, x, env=io)), 0) : (length(m.captures[1]), length(m.captures[2])) end function alignment(io::IO, x::Rational) m = match(r"^(.*?/)(/.*)$", sprint(0, show, x, env=io)) m === nothing ? (length(sprint(0, show, x, env=io)), 0) : (length(m.captures[1]), length(m.captures[2])) end const undef_ref_str = "#undef" const undef_ref_alignment = (3,3) """ `alignment(X, rows, cols, cols_if_complete, cols_otherwise, sep)` returns the alignment for specified parts of array `X`, returning the (left,right) info. It will look in X's `rows`, `cols` (both lists of indices) and figure out what's needed to be fully aligned, for example looking all the way down a column and finding out the maximum size of each element. Parameter `sep::Integer` is number of spaces to put between elements. `cols_if_complete` and `cols_otherwise` indicate screen width to use. Alignment is reported as a vector of (left,right) tuples, one for each column going across the screen. """ function alignment(io::IO, X::AbstractVecOrMat, rows::AbstractVector, cols::AbstractVector, cols_if_complete::Integer, cols_otherwise::Integer, sep::Integer) a = Tuple{Int, Int}[] for j in cols # need to go down each column one at a time l = r = 0 for i in rows # plumb down and see what largest element sizes are if isassigned(X,i,j) aij = alignment(io, X[i,j]) else aij = undef_ref_alignment end l = max(l, aij[1]) # left characters r = max(r, aij[2]) # right characters end push!(a, (l, r)) # one tuple per column of X, pruned to screen width if length(a) > 1 && sum(map(sum,a)) + sep*length(a) >= cols_if_complete pop!(a) # remove this latest tuple if we're already beyond screen width break end end if 1 < length(a) < length(indices(X,2)) while sum(map(sum,a)) + sep*length(a) >= cols_otherwise pop!(a) end end return a end """ Unexported convenience function used in body of `replace_in_print_matrix` methods. By default returns a string of the same width as original with a centered cdot, used in printing of structural zeros of structured matrices. Accept keyword args `c` for alternate single character marker. """ function replace_with_centered_mark(s::AbstractString;c::Char = '⋅') N = length(s) return join(setindex!([" " for i=1:N],string(c),ceil(Int,N/2))) end """ `print_matrix_row(io, X, A, i, cols, sep)` produces the aligned output for a single matrix row X[i, cols] where the desired list of columns is given. The corresponding alignment A is used, and the separation between elements is specified as string sep. `print_matrix_row` will also respect compact output for elements. """ function print_matrix_row(io::IO, X::AbstractVecOrMat, A::Vector, i::Integer, cols::AbstractVector, sep::AbstractString) isempty(A) || first(indices(cols,1)) == 1 || throw(DimensionMismatch("indices of cols ($(indices(cols,1))) must start at 1")) for k = 1:length(A) j = cols[k] if isassigned(X,Int(i),Int(j)) # isassigned accepts only `Int` indices x = X[i,j] a = alignment(io, x) sx = sprint(0, show, x, env=io) else a = undef_ref_alignment sx = undef_ref_str end l = repeat(" ", A[k][1]-a[1]) # pad on left and right as needed r = repeat(" ", A[k][2]-a[2]) prettysx = replace_in_print_matrix(X,i,j,sx) print(io, l, prettysx, r) if k < length(A); print(io, sep); end end end """ `print_matrix_vdots` is used to show a series of vertical ellipsis instead of a bunch of rows for long matrices. Not only is the string vdots shown but it also repeated every M elements if desired. """ function print_matrix_vdots(io::IO, vdots::AbstractString, A::Vector, sep::AbstractString, M::Integer, m::Integer) for k = 1:length(A) w = A[k][1] + A[k][2] if k % M == m l = repeat(" ", max(0, A[k][1]-length(vdots))) r = repeat(" ", max(0, w-length(vdots)-length(l))) print(io, l, vdots, r) else print(io, repeat(" ", w)) end if k < length(A); print(io, sep); end end end """ print_matrix(io::IO, mat, pre, sep, post, hdots, vdots, ddots, hmod, vmod) Prints a matrix with limited output size. If `io` sets `:limit` to true, then only the corners of the matrix are printed, separated with vertical, horizontal, and diagonal ellipses as appropriate. Optional arguments are string pre (printed before the matrix, e.g. an opening bracket) which will cause a corresponding same-size indent on following rows, and string post (printed at the end of the last row of the matrix). Also options to use different ellipsis characters hdots, vdots, ddots. These are repeated every hmod or vmod elements. """ function print_matrix(io::IO, X::AbstractVecOrMat, pre::AbstractString = " ", # pre-matrix string sep::AbstractString = " ", # separator between elements post::AbstractString = "", # post-matrix string hdots::AbstractString = " \u2026 ", vdots::AbstractString = "\u22ee", ddots::AbstractString = " \u22f1 ", hmod::Integer = 5, vmod::Integer = 5) if !get(io, :limit, false) screenheight = screenwidth = typemax(Int) else sz = displaysize(io) screenheight, screenwidth = sz[1] - 4, sz[2] end screenwidth -= length(pre) + length(post) presp = repeat(" ", length(pre)) # indent each row to match pre string postsp = "" @assert strwidth(hdots) == strwidth(ddots) sepsize = length(sep) rowsA, colsA = indices(X,1), indices(X,2) m, n = length(rowsA), length(colsA) # To figure out alignments, only need to look at as many rows as could # fit down screen. If screen has at least as many rows as A, look at A. # If not, then we only need to look at the first and last chunks of A, # each half a screen height in size. halfheight = div(screenheight,2) if m > screenheight rowsA = [rowsA[1:halfheight]; rowsA[m-div(screenheight-1,2)+1:m]] end # Similarly for columns, only necessary to get alignments for as many # columns as could conceivably fit across the screen maxpossiblecols = div(screenwidth, 1+sepsize) if n > maxpossiblecols colsA = [colsA[1:maxpossiblecols]; colsA[(n-maxpossiblecols+1):n]] end A = alignment(io, X, rowsA, colsA, screenwidth, screenwidth, sepsize) # Nine-slicing is accomplished using print_matrix_row repeatedly if m <= screenheight # rows fit vertically on screen if n <= length(A) # rows and cols fit so just print whole matrix in one piece for i in rowsA print(io, i == first(rowsA) ? pre : presp) print_matrix_row(io, X,A,i,colsA,sep) print(io, i == last(rowsA) ? post : postsp) if i != last(rowsA); println(io); end end else # rows fit down screen but cols don't, so need horizontal ellipsis c = div(screenwidth-length(hdots)+1,2)+1 # what goes to right of ellipsis Ralign = reverse(alignment(io, X, rowsA, reverse(colsA), c, c, sepsize)) # alignments for right c = screenwidth - sum(map(sum,Ralign)) - (length(Ralign)-1)*sepsize - length(hdots) Lalign = alignment(io, X, rowsA, colsA, c, c, sepsize) # alignments for left of ellipsis for i in rowsA print(io, i == first(rowsA) ? pre : presp) print_matrix_row(io, X,Lalign,i,colsA[1:length(Lalign)],sep) print(io, (i - first(rowsA)) % hmod == 0 ? hdots : repeat(" ", length(hdots))) print_matrix_row(io, X,Ralign,i,n-length(Ralign)+colsA,sep) print(io, i == last(rowsA) ? post : postsp) if i != last(rowsA); println(io); end end end else # rows don't fit so will need vertical ellipsis if n <= length(A) # rows don't fit, cols do, so only vertical ellipsis for i in rowsA print(io, i == first(rowsA) ? pre : presp) print_matrix_row(io, X,A,i,colsA,sep) print(io, i == last(rowsA) ? post : postsp) if i != rowsA[end]; println(io); end if i == rowsA[halfheight] print(io, i == first(rowsA) ? pre : presp) print_matrix_vdots(io, vdots,A,sep,vmod,1) println(io, i == last(rowsA) ? post : postsp) end end else # neither rows nor cols fit, so use all 3 kinds of dots c = div(screenwidth-length(hdots)+1,2)+1 Ralign = reverse(alignment(io, X, rowsA, reverse(colsA), c, c, sepsize)) c = screenwidth - sum(map(sum,Ralign)) - (length(Ralign)-1)*sepsize - length(hdots) Lalign = alignment(io, X, rowsA, colsA, c, c, sepsize) r = mod((length(Ralign)-n+1),vmod) # where to put dots on right half for i in rowsA print(io, i == first(rowsA) ? pre : presp) print_matrix_row(io, X,Lalign,i,colsA[1:length(Lalign)],sep) print(io, (i - first(rowsA)) % hmod == 0 ? hdots : repeat(" ", length(hdots))) print_matrix_row(io, X,Ralign,i,n-length(Ralign)+colsA,sep) print(io, i == last(rowsA) ? post : postsp) if i != rowsA[end]; println(io); end if i == rowsA[halfheight] print(io, i == first(rowsA) ? pre : presp) print_matrix_vdots(io, vdots,Lalign,sep,vmod,1) print(io, ddots) print_matrix_vdots(io, vdots,Ralign,sep,vmod,r) println(io, i == last(rowsA) ? post : postsp) end end end end end """ summary(x) Return a string giving a brief description of a value. By default returns `string(typeof(x))`, e.g. [`Int64`](@ref). For arrays, returns a string of size and type info, e.g. `10-element Array{Int64,1}`. ```jldoctest julia> summary(1) "Int64" julia> summary(zeros(2)) "2-element Array{Float64,1}" ``` """ summary(x) = string(typeof(x)) # e.g. Int64 # sizes such as 0-dimensional, 4-dimensional, 2x3 dims2string(d::Dims) = isempty(d) ? "0-dimensional" : length(d) == 1 ? "$(d[1])-element" : join(map(string,d), '×') inds2string(inds::Indices) = join(map(string,inds), '×') # anything array-like gets summarized e.g. 10-element Array{Int64,1} summary(a::AbstractArray) = _summary(a, indices(a)) _summary(a, inds::Tuple{Vararg{OneTo}}) = string(dims2string(length.(inds)), " ", typeof(a)) _summary(a, inds) = string(typeof(a), " with indices ", inds2string(inds)) # n-dimensional arrays function show_nd(io::IO, a::AbstractArray, print_matrix, label_slices) limit::Bool = get(io, :limit, false) if isempty(a) return end tailinds = tail(tail(indices(a))) nd = ndims(a)-2 for I in CartesianRange(tailinds) idxs = I.I if limit for i = 1:nd ii = idxs[i] ind = tailinds[i] if length(ind) > 10 if ii == ind[4] && all(d->idxs[d]==first(tailinds[d]),1:i-1) for j=i+1:nd szj = length(indices(a, j+2)) indj = tailinds[j] if szj>10 && first(indj)+2 < idxs[j] <= last(indj)-3 @goto skip end end #println(io, idxs) print(io, "...\n\n") @goto skip end if ind[3] < ii <= ind[end-3] @goto skip end end end end if label_slices print(io, "[:, :, ") for i = 1:(nd-1); print(io, "$(idxs[i]), "); end println(io, idxs[end], "] =") end slice = view(a, indices(a,1), indices(a,2), idxs...) print_matrix(io, slice) print(io, idxs == map(last,tailinds) ? "" : "\n\n") @label skip end end """ `print_matrix_repr(io, X)` prints matrix X with opening and closing square brackets. """ function print_matrix_repr(io, X::AbstractArray) limit = get(io, :limit, false)::Bool compact, prefix = array_eltype_show_how(X) if compact && !haskey(io, :compact) io = IOContext(io, :compact => compact) end indr, indc = indices(X,1), indices(X,2) nr, nc = length(indr), length(indc) rdots, cdots = false, false rr1, rr2 = UnitRange{Int}(indr), 1:0 cr1, cr2 = UnitRange{Int}(indc), 1:0 if limit if nr > 4 rr1, rr2 = rr1[1:2], rr1[nr-1:nr] rdots = true end if nc > 4 cr1, cr2 = cr1[1:2], cr1[nc-1:nc] cdots = true end end print(io, prefix, "[") for rr in (rr1, rr2) for i in rr for cr in (cr1, cr2) for j in cr j > first(cr) && print(io, " ") if !isassigned(X,i,j) print(io, undef_ref_str) else el = X[i,j] show(io, el) end end if last(cr) == last(indc) i < last(indr) && print(io, "; ") elseif cdots print(io, " \u2026 ") end end end last(rr) != nr && rdots && print(io, "\u2026 ; ") end print(io, "]") end show(io::IO, X::AbstractArray) = showarray(io, X, true) repremptyarray(io::IO, X::Array{T}) where {T} = print(io, "Array{$T}(", join(size(X),','), ')') repremptyarray(io, X) = nothing # by default, we don't know this constructor function showarray(io::IO, X::AbstractArray, repr::Bool = true; header = true) if repr && ndims(X) == 1 return show_vector(io, X, "[", "]") end if !haskey(io, :compact) io = IOContext(io, :compact => true) end if !repr && get(io, :limit, false) && eltype(X) === Method # override usual show method for Vector{Method}: don't abbreviate long lists io = IOContext(io, :limit => false) end (!repr && header) && print(io, summary(X)) if !isempty(X) (!repr && header) && println(io, ":") if ndims(X) == 0 if isassigned(X) return show(io, X[]) else return print(io, undef_ref_str) end end if repr if ndims(X) <= 2 print_matrix_repr(io, X) else show_nd(io, X, print_matrix_repr, false) end else punct = (" ", " ", "") if ndims(X) <= 2 print_matrix(io, X, punct...) else show_nd(io, X, (io, slice) -> print_matrix(io, slice, punct...), !repr) end end elseif repr repremptyarray(io, X) end end showall(x) = showall(STDOUT, x) function showall(io::IO, x) if !get(io, :limit, false) show(io, x) else show(IOContext(io, :limit => false), x) end end showcompact(x) = showcompact(STDOUT, x) function showcompact(io::IO, x) if get(io, :compact, false) show(io, x) else show(IOContext(io, :compact => true), x) end end # returns compact, prefix function array_eltype_show_how(X) e = eltype(X) if print_without_params(e) str = string(unwrap_unionall(e).name) # Print "Array" rather than "Array{T,N}" else str = string(e) end # Types hard-coded here are those which are created by default for a given syntax isleaftype(e), (!isempty(X) && (e===Float64 || e===Int || e===Char) ? "" : str) end function show_vector(io::IO, v, opn, cls) compact, prefix = array_eltype_show_how(v) limited = get(io, :limit, false) if compact && !haskey(io, :compact) io = IOContext(io, :compact => compact) end print(io, prefix) if limited && _length(v) > 20 inds = indices1(v) show_delim_array(io, v, opn, ",", "", false, inds[1], inds[1]+9) print(io, " \u2026 ") show_delim_array(io, v, "", ",", cls, false, inds[end-9], inds[end]) else show_delim_array(io, v, opn, ",", cls, false) end end # printing BitArrays # (following functions not exported - mainly intended for debug) function print_bit_chunk(io::IO, c::UInt64, l::Integer = 64) for s = 0:l-1 d = (c >>> s) & 1 print(io, "01"[d + 1]) if (s + 1) & 7 == 0 print(io, " ") end end end print_bit_chunk(c::UInt64, l::Integer) = print_bit_chunk(STDOUT, c, l) print_bit_chunk(c::UInt64) = print_bit_chunk(STDOUT, c) function bitshow(io::IO, B::BitArray) isempty(B) && return Bc = B.chunks for i = 1:length(Bc)-1 print_bit_chunk(io, Bc[i]) print(io, ": ") end l = _mod64(length(B)-1) + 1 print_bit_chunk(io, Bc[end], l) end bitshow(B::BitArray) = bitshow(STDOUT, B) bitstring(B::BitArray) = sprint(bitshow, B)