Skip to content

Commit

Permalink
add :color attribute to IOContext, prefer get(io,:color,false) to Bas…
Browse files Browse the repository at this point in the history
…e.have_color (#25067)
  • Loading branch information
stevengj authored and StefanKarpinski committed Dec 18, 2017
1 parent 5e50550 commit 88cdf44
Show file tree
Hide file tree
Showing 20 changed files with 146 additions and 194 deletions.
4 changes: 4 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,10 @@ Library improvements
* The function `chop` now accepts two arguments `head` and `tail` allowing to specify
number of characters to remove from the head and tail of the string ([#24126]).

* `get(io, :color, false)` can now be used to query whether a stream `io` supports
[ANSI color codes](https://en.wikipedia.org/wiki/ANSI_escape_code) ([#25067]),
rather than using the undocumented `Base.have_color` global flag.

* Functions `first` and `last` now accept `nchar` argument for `AbstractString`.
If this argument is used they return a string consisting of first/last `nchar`
characters from the original string ([#23960]).
Expand Down
13 changes: 7 additions & 6 deletions base/logging.jl
Original file line number Diff line number Diff line change
Expand Up @@ -485,16 +485,17 @@ function handle_message(logger::SimpleLogger, level, message, _module, group, id
level < Warn ? :cyan :
level < Error ? :yellow : :red
buf = IOBuffer()
print_with_color(color, buf, first(levelstr), "- ", bold=true)
iob = IOContext(buf, logger.stream)
print_with_color(color, iob, first(levelstr), "- ", bold=true)
msglines = split(string(message), '\n')
for i in 1:length(msglines)-1
println(buf, msglines[i])
print_with_color(color, buf, "| ", bold=true)
println(iob, msglines[i])
print_with_color(color, iob, "| ", bold=true)
end
println(buf, msglines[end], " -", levelstr, ":", _module, ":", basename(filepath), ":", line)
println(iob, msglines[end], " -", levelstr, ":", _module, ":", basename(filepath), ":", line)
for (key,val) in pairs(kwargs)
print_with_color(color, buf, "| ", bold=true)
println(buf, key, " = ", val)
print_with_color(color, iob, "| ", bold=true)
println(iob, key, " = ", val)
end
write(logger.stream, take!(buf))
nothing
Expand Down
6 changes: 0 additions & 6 deletions base/markdown/Markdown.jl
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,4 @@ macro doc_str(s::AbstractString, t...)
docexpr(__source__, __module__, s, t...)
end

function Base.display(d::Base.REPL.REPLDisplay, mds::Vector{MD})
for md in mds
display(d, md)
end
end

end
16 changes: 8 additions & 8 deletions base/markdown/render/terminal/formatting.jl
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@ const text_formats = Dict(
:negative => ("\e[7m", "\e[27m"))

function with_output_format(f::Function, formats::Vector{Symbol}, io::IO, args...)
Base.have_color && for format in formats
get(io, :color, false) && for format in formats
haskey(text_formats, format) &&
print(io, text_formats[format][1])
end
try f(io, args...)
finally
Base.have_color && for format in formats
get(io, :color, false) && for format in formats
haskey(text_formats, format) &&
print(io, text_formats[format][2])
end
Expand Down Expand Up @@ -58,9 +58,9 @@ words(s) = split(s, " ")
lines(s) = split(s, "\n")

# This could really be more efficient
function wrapped_lines(s::AbstractString; width = 80, i = 0)
function wrapped_lines(io::IO, s::AbstractString; width = 80, i = 0)
if ismatch(r"\n", s)
return vcat(map(s->wrapped_lines(s, width = width, i = i), split(s, "\n"))...)
return vcat(map(s->wrapped_lines(io, s, width = width, i = i), split(s, "\n"))...)
end
ws = words(s)
lines = AbstractString[ws[1]]
Expand All @@ -78,11 +78,11 @@ function wrapped_lines(s::AbstractString; width = 80, i = 0)
return lines
end

wrapped_lines(f::Function, args...; width = 80, i = 0) =
wrapped_lines(sprint(f, args...), width = width, i = 0)
wrapped_lines(io::IO, f::Function, args...; width = 80, i = 0) =
wrapped_lines(io, sprint(f, args...; context=io), width = width, i = 0)

function print_wrapped(io::IO, s...; width = 80, pre = "", i = 0)
lines = wrapped_lines(s..., width = width, i = i)
lines = wrapped_lines(io, s..., width = width, i = i)
println(io, lines[1])
for line in lines[2:end]
println(io, pre, line)
Expand All @@ -93,7 +93,7 @@ end
print_wrapped(f::Function, io::IO, args...; kws...) = print_wrapped(io, f, args...; kws...)

function print_centred(io::IO, s...; columns = 80, width = columns)
lines = wrapped_lines(s..., width = width)
lines = wrapped_lines(io, s..., width = width)
for line in lines
print(io, " "^(div(columns-ansi_length(line), 2)))
println(io, line)
Expand Down
15 changes: 7 additions & 8 deletions base/markdown/render/terminal/render.jl
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ function term(io::IO, md::Paragraph, columns)
end

function term(io::IO, md::BlockQuote, columns)
s = sprint(term, md.content, columns - 10)
s = sprint(term, md.content, columns - 10; context=io)
for line in split(rstrip(s), "\n")
println(io, " "^margin, "|", line)
end
Expand All @@ -34,7 +34,7 @@ function term(io::IO, md::Admonition, columns)
print(io, " "^margin, "| ")
with_output_format(:bold, print, io, isempty(md.title) ? md.category : md.title)
println(io, "\n", " "^margin, "|")
s = sprint(term, md.content, columns - 10)
s = sprint(term, md.content, columns - 10; context=io)
for line in split(rstrip(s), "\n")
println(io, " "^margin, "|", line)
end
Expand All @@ -44,7 +44,7 @@ function term(io::IO, f::Footnote, columns)
print(io, " "^margin, "| ")
with_output_format(:bold, print, io, "[^$(f.id)]")
println(io, "\n", " "^margin, "|")
s = sprint(term, f.text, columns - 10)
s = sprint(term, f.text, columns - 10; context=io)
for line in split(rstrip(s), "\n")
println(io, " "^margin, "|", line)
end
Expand All @@ -61,7 +61,7 @@ function term(io::IO, md::List, columns)
end

function _term_header(io::IO, md, char, columns)
text = terminline(md.text)
text = terminline_string(io, md.text)
with_output_format(:bold, io) do io
print(io, " "^(margin))
line_no, lastline_width = print_wrapped(io, text,
Expand Down Expand Up @@ -103,7 +103,7 @@ term(io::IO, x, _) = show(io, MIME"text/plain"(), x)

# Inline Content

terminline(md) = sprint(terminline, md)
terminline_string(io::IO, md) = sprint(terminline, md; context=io)

terminline(io::IO, content...) = terminline(io, collect(content))

Expand Down Expand Up @@ -137,7 +137,7 @@ terminline(io::IO, f::Footnote) = with_output_format(:bold, terminline, io, "[^$

function terminline(io::IO, md::Link)
url = !Base.startswith(md.url, "@ref") ? " ($(md.url))" : ""
text = terminline(md.text)
text = terminline_string(io, md.text)
terminline(io, text, url)
end

Expand All @@ -148,5 +148,4 @@ end
terminline(io::IO, x) = show(io, MIME"text/plain"(), x)

# Show in terminal

Base.display(d::Base.REPL.REPLDisplay, md::MD) = term(Base.REPL.outstream(d.repl), md)
Base.show(io::IO, ::MIME"text/plain", md::MD) = (term(io, md); nothing)
2 changes: 1 addition & 1 deletion base/repl/REPL.jl
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ end

function display(d::REPLDisplay, mime::MIME"text/plain", x)
io = outstream(d.repl)
Base.have_color && write(io, answer_color(d.repl))
get(io, :color, false) && write(io, answer_color(d.repl))
show(IOContext(io, :limit => true), mime, x)
println(io)
end
Expand Down
3 changes: 3 additions & 0 deletions base/repl/Terminals.jl
Original file line number Diff line number Diff line change
Expand Up @@ -167,4 +167,7 @@ else
end
end

# use cached value of have_color
Base.get(::TTYTerminal, k::Symbol, default) = k === :color ? Base.have_color : default

end # module
45 changes: 23 additions & 22 deletions base/replutil.jl
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ end

function showerror(io::IO, ex, bt; backtrace=true)
try
with_output_color(have_color ? error_color() : :nothing, io) do io
with_output_color(get(io, :color, false) ? error_color() : :nothing, io) do io
showerror(io, ex)
end
finally
Expand Down Expand Up @@ -488,6 +488,7 @@ function show_method_candidates(io::IO, ex::MethodError, kwargs::NamedTuple = Na
for (func,arg_types_param) in funcs
for method in methods(func)
buf = IOBuffer()
iob = IOContext(buf, io)
tv = Any[]
sig0 = method.sig
if Base.is_default_method(method)
Expand All @@ -499,20 +500,20 @@ function show_method_candidates(io::IO, ex::MethodError, kwargs::NamedTuple = Na
end
s1 = sig0.parameters[1]
sig = sig0.parameters[2:end]
print(buf, " ")
print(iob, " ")
if !isa(func, s1)
# function itself doesn't match
return
else
# TODO: use the methodshow logic here
use_constructor_syntax = isa(func, Type)
print(buf, use_constructor_syntax ? func : typeof(func).name.mt.name)
print(iob, use_constructor_syntax ? func : typeof(func).name.mt.name)
end
print(buf, "(")
print(iob, "(")
t_i = copy(arg_types_param)
right_matches = 0
for i = 1 : min(length(t_i), length(sig))
i > 1 && print(buf, ", ")
i > 1 && print(iob, ", ")
# If isvarargtype then it checks whether the rest of the input arguments matches
# the varargtype
if Base.isvarargtype(sig[i])
Expand All @@ -529,20 +530,20 @@ function show_method_candidates(io::IO, ex::MethodError, kwargs::NamedTuple = Na
# the type of the first argument is not matched.
t_in === Union{} && special && i == 1 && break
if t_in === Union{}
if Base.have_color
Base.with_output_color(Base.error_color(), buf) do buf
print(buf, "::$sigstr")
if get(io, :color, false)
Base.with_output_color(Base.error_color(), iob) do iob
print(iob, "::$sigstr")
end
else
print(buf, "!Matched::$sigstr")
print(iob, "!Matched::$sigstr")
end
# If there is no typeintersect then the type signature from the method is
# inserted in t_i this ensures if the type at the next i matches the type
# signature then there will be a type intersect
t_i[i] = sig[i]
else
right_matches += j==i ? 1 : 0
print(buf, "::$sigstr")
print(iob, "::$sigstr")
end
end
special && right_matches==0 && return # continue the do-block
Expand All @@ -569,26 +570,26 @@ function show_method_candidates(io::IO, ex::MethodError, kwargs::NamedTuple = Na
sigstr = string(sigtype)
end
if !((min(length(t_i), length(sig)) == 0) && k==1)
print(buf, ", ")
print(iob, ", ")
end
if Base.have_color
Base.with_output_color(Base.error_color(), buf) do buf
print(buf, "::$sigstr")
if get(io, :color, false)
Base.with_output_color(Base.error_color(), iob) do iob
print(iob, "::$sigstr")
end
else
print(buf, "!Matched::$sigstr")
print(iob, "!Matched::$sigstr")
end
end
end
kwords = Symbol[]
if isdefined(ft.name.mt, :kwsorter)
kwsorter_t = typeof(ft.name.mt.kwsorter)
kwords = kwarg_decl(method, kwsorter_t)
length(kwords) > 0 && print(buf, "; ", join(kwords, ", "))
length(kwords) > 0 && print(iob, "; ", join(kwords, ", "))
end
print(buf, ")")
show_method_params(buf, tv)
print(buf, " at ", method.file, ":", method.line)
print(iob, ")")
show_method_params(iob, tv)
print(iob, " at ", method.file, ":", method.line)
if !isempty(kwargs)
unexpected = Symbol[]
if isempty(kwords) || !(any(endswith(string(kword), "...") for kword in kwords))
Expand All @@ -599,14 +600,14 @@ function show_method_candidates(io::IO, ex::MethodError, kwargs::NamedTuple = Na
end
end
if !isempty(unexpected)
Base.with_output_color(Base.error_color(), buf) do buf
Base.with_output_color(Base.error_color(), iob) do iob
plur = length(unexpected) > 1 ? "s" : ""
print(buf, " got unsupported keyword argument$plur \"", join(unexpected, "\", \""), "\"")
print(iob, " got unsupported keyword argument$plur \"", join(unexpected, "\", \""), "\"")
end
end
end
if ex.world < min_world(method)
print(buf, " (method too new to be called from this world context.)")
print(iob, " (method too new to be called from this world context.)")
end
# TODO: indicate if it's in the wrong world
push!(lines, (buf, right_matches))
Expand Down
12 changes: 8 additions & 4 deletions base/show.jl
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ struct IOContext{IO_t <: IO} <: AbstractPipe
end
end

unwrapcontext(io::IO) = io, ImmutableDict{Symbol,Any}()
# (Note that TTY and TTYTerminal io types have a :color property.)
unwrapcontext(io::IO) = io, get(io,:color,false) ? ImmutableDict{Symbol,Any}(:color, true) : ImmutableDict{Symbol,Any}()
unwrapcontext(io::IOContext) = io.io, io.dict

function IOContext(io::IO, dict::ImmutableDict)
Expand Down Expand Up @@ -67,6 +68,9 @@ The following properties are in common use:
can be avoided (e.g. `[Float16(0)]` can be shown as "Float16[0.0]" instead
of "Float16[Float16(0.0)]" : while displaying the elements of the array, the `:typeinfo`
property will be set to `Float16`).
- `:color`: Boolean specifying whether ANSI color/escape codes are supported/expected.
By default, this is determined by whether `io` is a compatible terminal and by any
`--color` command-line flag when `julia` was launched.
# Examples
```jldoctest
Expand Down Expand Up @@ -715,7 +719,7 @@ function show_expr_type(io::IO, @nospecialize(ty), emph::Bool)
end
end

emphasize(io, str::AbstractString) = have_color ?
emphasize(io, str::AbstractString) = get(io, :color, false) ?
print_with_color(Base.error_color(), io, str; bold = true) :
print(io, Unicode.uppercase(str))

Expand Down Expand Up @@ -1253,7 +1257,7 @@ 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
color = get(io, :color, false) && get(io, :backtrace, false) ? stackframe_function_color() : :nothing
if sig === Tuple
Base.print_with_color(color, io, name, "(...)")
return
Expand All @@ -1274,7 +1278,7 @@ function show_tuple_as_call(io::IO, name::Symbol, sig::Type)
end
end
first = true
print_style = have_color && get(io, :backtrace, false) ? :bold : :nothing
print_style = get(io, :color, false) && 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, ", ")
Expand Down
4 changes: 2 additions & 2 deletions base/stacktraces.jl
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,7 @@ function show_spec_linfo(io::IO, frame::StackFrame)
elseif frame.func === top_level_scope_sym
print(io, "top-level scope")
else
print_with_color(Base.have_color && get(io, :backtrace, false) ? Base.stackframe_function_color() : :nothing, io, string(frame.func))
print_with_color(get(io, :color, false) && get(io, :backtrace, false) ? Base.stackframe_function_color() : :nothing, io, string(frame.func))
end
elseif frame.linfo isa Core.MethodInstance
if isa(frame.linfo.def, Method)
Expand All @@ -323,7 +323,7 @@ function show(io::IO, frame::StackFrame; full_path::Bool=false)
if frame.file !== empty_sym
file_info = full_path ? string(frame.file) : basename(string(frame.file))
print(io, " at ")
Base.with_output_color(Base.have_color && get(io, :backtrace, false) ? Base.stackframe_lineinfo_color() : :nothing, io) do io
Base.with_output_color(get(io, :color, false) && get(io, :backtrace, false) ? Base.stackframe_lineinfo_color() : :nothing, io) do io
print(io, file_info, ":")
if frame.line >= 0
print(io, frame.line)
Expand Down
1 change: 1 addition & 0 deletions base/stream.jl
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,7 @@ function displaysize(io::TTY)
return h, w
end

get(::TTY, k::Symbol, default) = k === :color ? have_color : default

### Libuv callbacks ###

Expand Down
9 changes: 5 additions & 4 deletions base/util.jl
Original file line number Diff line number Diff line change
Expand Up @@ -319,12 +319,13 @@ end

function with_output_color(f::Function, color::Union{Int, Symbol}, io::IO, args...; bold::Bool = false)
buf = IOBuffer()
have_color && bold && print(buf, text_colors[:bold])
have_color && print(buf, get(text_colors, color, color_normal))
iscolor = get(io, :color, false)
iscolor && bold && print(buf, text_colors[:bold])
iscolor && print(buf, get(text_colors, color, color_normal))
try f(IOContext(buf, io), args...)
finally
have_color && color != :nothing && print(buf, get(disable_text_style, color, text_colors[:default]))
have_color && (bold || color == :bold) && print(buf, disable_text_style[:bold])
iscolor && color != :nothing && print(buf, get(disable_text_style, color, text_colors[:default]))
iscolor && (bold || color == :bold) && print(buf, disable_text_style[:bold])
print(io, String(take!(buf)))
end
end
Expand Down
2 changes: 1 addition & 1 deletion base/version.jl
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@ function banner(io::IO = STDOUT)
end
commit_date = !isempty(GIT_VERSION_INFO.date_string) ? " ($(GIT_VERSION_INFO.date_string))" : ""

if have_color
if get(io, :color, false)
c = text_colors
tx = c[:normal] # text
jl = c[:normal] # julia
Expand Down
Loading

0 comments on commit 88cdf44

Please sign in to comment.