Skip to content

Commit

Permalink
introduce JULIA_ERROR_COLOR to control how errors are printed
Browse files Browse the repository at this point in the history
  • Loading branch information
KristofferC committed Oct 28, 2016
1 parent 557e3c0 commit 39398fd
Show file tree
Hide file tree
Showing 13 changed files with 99 additions and 65 deletions.
6 changes: 6 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,12 @@ Library improvements
* BitArrays can now be constructed from arbitrary iterables, in particular from generator expressions,
e.g. `BitArray(isodd(x) for x = 1:100)` ([#19018]).

* The function `print_with_color` no longer prints text in bold by default. Instead, the function now takes
a keyword argument `bold::Bool` which determines whether to print in bold or not.
On some terminals, printing a color in non bold results in slightly darker colors being printed than when printing in bold.
Therefore, light versions of the colors are now supported.
For the available colors see the help entry on `print_with_color.

Compiler/Runtime improvements
-----------------------------

Expand Down
1 change: 1 addition & 0 deletions base/LineEdit.jl
Original file line number Diff line number Diff line change
Expand Up @@ -629,6 +629,7 @@ function write_prompt(terminal, p::Prompt)
prefix = isa(p.prompt_prefix,Function) ? p.prompt_prefix() : p.prompt_prefix
suffix = isa(p.prompt_suffix,Function) ? p.prompt_suffix() : p.prompt_suffix
write(terminal, prefix)
write(terminal, Base.text_colors[:bold])
write(terminal, p.prompt)
write(terminal, Base.text_colors[:normal])
write(terminal, suffix)
Expand Down
4 changes: 2 additions & 2 deletions base/REPL.jl
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,8 @@ function ip_matches_func(ip, func::Symbol)
end

function display_error(io::IO, er, bt)
Base.with_output_color(:red, io) do io
print(io, "ERROR: ")
print_with_color(Base.error_color(), io, "ERROR: "; bold = true)
Base.with_output_color(Base.error_color(), io) do io
# remove REPL-related frames from interactive printing
eval_ind = findlast(addr->ip_matches_func(addr, :eval), bt)
if eval_ind != 0
Expand Down
59 changes: 40 additions & 19 deletions base/client.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,39 @@
## and REPL

const text_colors = AnyDict(
:black => "\033[1m\033[30m",
:red => "\033[1m\033[31m",
:green => "\033[1m\033[32m",
:yellow => "\033[1m\033[33m",
:blue => "\033[1m\033[34m",
:magenta => "\033[1m\033[35m",
:cyan => "\033[1m\033[36m",
:white => "\033[1m\033[37m",
:normal => "\033[0m",
:bold => "\033[1m",
:black => "\033[30m",
:red => "\033[31m",
:green => "\033[32m",
:yellow => "\033[33m",
:blue => "\033[34m",
:magenta => "\033[35m",
:cyan => "\033[36m",
:white => "\033[37m",
:light_black => "\033[90m",
:light_red => "\033[91m",
:light_green => "\033[92m",
:light_yellow => "\033[93m",
:light_blue => "\033[94m",
:light_magenta => "\033[95m",
:light_cyan => "\033[96m",
:normal => "\033[0m",
:default => "\033[39m",
:bold => "\033[1m",
)

const disable_text_style = AnyDict(
:bold => "\033[22m",
:normal => "",
:default => "",
)

for i in 0:255
text_colors[i] = "\033[1m\033[38;5;$(i)m"
text_colors[i] = "\033[38;5;$(i)m"
end

# Create a docstring with an automatically generated list
# of colors.
const possible_formatting_symbols = [:normal, :bold]
const possible_formatting_symbols = [:normal, :bold, :default]
available_text_colors = collect(Iterators.filter(x -> !isa(x, Integer), keys(text_colors)))
available_text_colors = cat(1,
sort(intersect(available_text_colors, possible_formatting_symbols), rev=true),
Expand All @@ -35,11 +49,15 @@ const available_text_colors_docstring =
"""Dictionary of color codes for the terminal.
Available colors are: $available_text_colors_docstring as well as the integers 0 to 255 inclusive.
The color `:default` will print text in the default color while the color `:normal`
will print text with all text properties (like boldness) reset.
"""
text_colors

have_color = false
default_color_warn = :red
default_color_warn = :light_red
default_color_error = :light_red
default_color_info = :cyan
if is_windows()
default_color_input = :normal
Expand All @@ -57,10 +75,13 @@ function repl_color(key, default)
haskey(text_colors, c_conv) ? c_conv : default
end

warn_color() = repl_color("JULIA_WARN_COLOR", default_color_warn)
info_color() = repl_color("JULIA_INFO_COLOR", default_color_info)
input_color() = text_colors[repl_color("JULIA_INPUT_COLOR", default_color_input)]
answer_color() = text_colors[repl_color("JULIA_ANSWER_COLOR", default_color_answer)]
error_color() = repl_color("JULIA_ERROR_COLOR", default_color_error)
warn_color() = repl_color("JULIA_WARN_COLOR" , default_color_warn)
info_color() = repl_color("JULIA_INFO_COLOR" , default_color_info)

# Print input and answer in bold.
input_color() = text_colors[:bold] * text_colors[repl_color("JULIA_INPUT_COLOR", default_color_input)]
answer_color() = text_colors[:bold] * text_colors[repl_color("JULIA_ANSWER_COLOR", default_color_answer)]

function repl_cmd(cmd, out)
shell = shell_split(get(ENV,"JULIA_SHELL",get(ENV,"SHELL","/bin/sh")))
Expand Down Expand Up @@ -101,8 +122,8 @@ end

display_error(er) = display_error(er, [])
function display_error(er, bt)
with_output_color(:red, STDERR) do io
print(io, "ERROR: ")
print_with_color(Base.error_color(), STDERR, "ERROR: "; bold = true)
with_output_color(Base.error_color(), STDERR) do io
showerror(io, er, bt)
println(io)
end
Expand Down
2 changes: 1 addition & 1 deletion base/pkg/entry.jl
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ function status(io::IO, pkg::AbstractString, ver::VersionNumber, fix::Bool)
LibGit2.isdirty(prepo) && push!(attrs,"dirty")
isempty(attrs) || print(io, " (",join(attrs,", "),")")
catch err
print_with_color(:red, io, " broken-repo (unregistered)")
print_with_color(Base.error_color(), io, " broken-repo (unregistered)")
finally
finalize(prepo)
end
Expand Down
6 changes: 3 additions & 3 deletions base/replutil.jl
Original file line number Diff line number Diff line change
Expand Up @@ -462,7 +462,7 @@ function show_method_candidates(io::IO, ex::MethodError, kwargs::Vector=Any[])
t_in === Union{} && special && i == 1 && break
if t_in === Union{}
if Base.have_color
Base.with_output_color(:red, buf) do buf
Base.with_output_color(Base.error_color(), buf) do buf
print(buf, "::$sigstr")
end
else
Expand Down Expand Up @@ -503,7 +503,7 @@ function show_method_candidates(io::IO, ex::MethodError, kwargs::Vector=Any[])
print(buf, ", ")
end
if Base.have_color
Base.with_output_color(:red, buf) do buf
Base.with_output_color(Base.error_color(), buf) do buf
print(buf, "::$sigstr")
end
else
Expand All @@ -529,7 +529,7 @@ function show_method_candidates(io::IO, ex::MethodError, kwargs::Vector=Any[])
end
end
if !isempty(unexpected)
Base.with_output_color(:red, buf) do buf
Base.with_output_color(Base.error_color(), buf) do buf
plur = length(unexpected) > 1 ? "s" : ""
print(buf, " got unsupported keyword argument$plur \"", join(unexpected, "\", \""), "\"")
end
Expand Down
2 changes: 1 addition & 1 deletion base/show.jl
Original file line number Diff line number Diff line change
Expand Up @@ -527,7 +527,7 @@ function show_expr_type(io::IO, ty, emph)
end
end

emphasize(io, str::AbstractString) = have_color ? print_with_color(:red, io, str) : print(io, uppercase(str))
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,':')
Expand Down
2 changes: 1 addition & 1 deletion base/task.jl
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ function task_done_hook(t::Task)
if !suppress_excp_printing(t)
let bt = t.backtrace
# run a new task to print the error for us
@schedule with_output_color(:red, STDERR) do io
@schedule with_output_color(Base.error_color(), STDERR) do io
print(io, "ERROR (unhandled task failure): ")
showerror(io, result, bt)
println(io)
Expand Down
34 changes: 17 additions & 17 deletions base/test.jl
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ immutable Pass <: Result
value
end
function Base.show(io::IO, t::Pass)
print_with_color(:green, io, "Test Passed\n")
print_with_color(:green, io, "Test Passed\n"; bold = true)
print(io, " Expression: ", t.orig_expr)
if t.test_type == :test_throws
# The correct type of exception was thrown
Expand All @@ -68,7 +68,7 @@ type Fail <: Result
value
end
function Base.show(io::IO, t::Fail)
print_with_color(:red, io, "Test Failed\n")
print_with_color(:light_red, io, "Test Failed\n"; bold = true)
print(io, " Expression: ", t.orig_expr)
if t.test_type == :test_throws_wrong
# An exception was thrown, but it was of the wrong type
Expand Down Expand Up @@ -100,7 +100,7 @@ type Error <: Result
backtrace
end
function Base.show(io::IO, t::Error)
print_with_color(:red, io, "Error During Test\n")
print_with_color(:light_red, io, "Error During Test\n"; bold = true)
if t.test_type == :test_nonbool
println(io, " Expression evaluated to non-Boolean")
println(io, " Expression: ", t.orig_expr)
Expand Down Expand Up @@ -138,7 +138,7 @@ type Broken <: Result
orig_expr
end
function Base.show(io::IO, t::Broken)
print_with_color(:yellow, io, "Test Broken\n")
print_with_color(:yellow, io, "Test Broken\n"; bold = true)
if t.test_type == :skipped
println(io, " Skipped: ", t.orig_expr)
else
Expand Down Expand Up @@ -423,7 +423,7 @@ record(ts::DefaultTestSet, t::Union{Pass,Broken}) = (push!(ts.results, t); t)
# but do not terminate. Print a backtrace.
function record(ts::DefaultTestSet, t::Union{Fail, Error})
if myid() == 1
print_with_color(:white, ts.description, ": ")
print_with_color(:white, ts.description, ": "; bold = true)
print(t)
# don't print the backtrace for Errors because it gets printed in the show
# method
Expand Down Expand Up @@ -476,21 +476,21 @@ function print_test_results(ts::DefaultTestSet, depth_pad=0)
# recursively walking the tree of test sets
align = max(get_alignment(ts, 0), length("Test Summary:"))
# Print the outer test set header once
print_with_color(:white, rpad("Test Summary:",align," "), " | ")
print_with_color(:white, rpad("Test Summary:",align," "), " | "; bold = true)
if pass_width > 0
print_with_color(:green, lpad("Pass",pass_width," "), " ")
print_with_color(:green, lpad("Pass",pass_width," "), " "; bold = true)
end
if fail_width > 0
print_with_color(:red, lpad("Fail",fail_width," "), " ")
print_with_color(:light_red, lpad("Fail",fail_width," "), " "; bold = true)
end
if error_width > 0
print_with_color(:red, lpad("Error",error_width," "), " ")
print_with_color(:light_red, lpad("Error",error_width," "), " "; bold = true)
end
if broken_width > 0
print_with_color(:yellow, lpad("Broken",broken_width," "), " ")
print_with_color(:yellow, lpad("Broken",broken_width," "), " "; bold = true)
end
if total_width > 0
print_with_color(:blue, lpad("Total",total_width, " "))
print_with_color(:blue, lpad("Total",total_width, " "); bold = true)
end
println()
# Recursively print a summary at every level
Expand Down Expand Up @@ -593,40 +593,40 @@ function print_counts(ts::DefaultTestSet, depth, align,

np = passes + c_passes
if np > 0
print_with_color(:green, lpad(string(np), pass_width, " "), " ")
print_with_color(:green, lpad(string(np), pass_width, " "), " "; bold = true)
elseif pass_width > 0
# No passes at this level, but some at another level
print(lpad(" ", pass_width), " ")
end

nf = fails + c_fails
if nf > 0
print_with_color(:red, lpad(string(nf), fail_width, " "), " ")
print_with_color(:light_red, lpad(string(nf), fail_width, " "), " "; bold = true)
elseif fail_width > 0
# No fails at this level, but some at another level
print(lpad(" ", fail_width), " ")
end

ne = errors + c_errors
if ne > 0
print_with_color(:red, lpad(string(ne), error_width, " "), " ")
print_with_color(:light_red, lpad(string(ne), error_width, " "), " "; bold = true)
elseif error_width > 0
# No errors at this level, but some at another level
print(lpad(" ", error_width), " ")
end

nb = broken + c_broken
if nb > 0
print_with_color(:yellow, lpad(string(nb), broken_width, " "), " ")
print_with_color(:yellow, lpad(string(nb), broken_width, " "), " "; bold = true)
elseif broken_width > 0
# None broken at this level, but some at another level
print(lpad(" ", broken_width), " ")
end

if np == 0 && nf == 0 && ne == 0 && nb == 0
print_with_color(:blue, "No tests")
print_with_color(:blue, "No tests"; bold = true)
else
print_with_color(:blue, lpad(string(subtotal), total_width, " "))
print_with_color(:blue, lpad(string(subtotal), total_width, " "); bold = true)
end
println()

Expand Down
32 changes: 19 additions & 13 deletions base/util.jl
Original file line number Diff line number Diff line change
Expand Up @@ -303,35 +303,39 @@ end

## printing with color ##

function with_output_color(f::Function, color::Union{Int, Symbol}, io::IO, args...)
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))
try f(IOContext(buf, io), args...)
finally
have_color && print(buf, color_normal)
have_color && print(buf, get(disable_text_style, color, text_colors[:default]))
have_color && (bold || color == :bold) && print(buf, disable_text_style[:bold])
print(io, takebuf_string(buf))
end
end

"""
print_with_color(color::Union{Symbol, Int}, [io], strings...)
print_with_color(color::Union{Symbol, Int}, [io], strings...; bold::Bool = false)
Print strings in a color specified as a symbol.
`color` may take any of the values $(Base.available_text_colors_docstring)
or an integer between 0 and 255 inclusive. Note that not all terminals support 256 colors.
If the keyword `bold` is given as `true`, the result will be printed in bold.
"""
print_with_color(color::Union{Int, Symbol}, io::IO, msg::AbstractString...) =
with_output_color(print, color, io, msg...)
print_with_color(color::Union{Int, Symbol}, msg::AbstractString...) =
print_with_color(color, STDOUT, msg...)
println_with_color(color::Union{Int, Symbol}, io::IO, msg::AbstractString...) =
with_output_color(println, color, io, msg...)
println_with_color(color::Union{Int, Symbol}, msg::AbstractString...) =
println_with_color(color, STDOUT, msg...)
print_with_color(color::Union{Int, Symbol}, io::IO, msg::AbstractString...; bold::Bool = false) =
with_output_color(print, color, io, msg...; bold = bold)
print_with_color(color::Union{Int, Symbol}, msg::AbstractString...; bold::Bool = false) =
print_with_color(color, STDOUT, msg...; bold = bold)
println_with_color(color::Union{Int, Symbol}, io::IO, msg::AbstractString...; bold::Bool = false) =
with_output_color(println, color, io, msg...; bold = bold)
println_with_color(color::Union{Int, Symbol}, msg::AbstractString...; bold::Bool = false) =
println_with_color(color, STDOUT, msg...; bold = bold)

## warnings and messages ##


"""
info(msg...; prefix="INFO: ")
Expand All @@ -349,7 +353,8 @@ MY INFO: hello world
```
"""
function info(io::IO, msg...; prefix="INFO: ")
println_with_color(info_color(), io, prefix, chomp(string(msg...)))
print_with_color(info_color(), io, prefix; bold = true)
println_with_color(info_color(), io, chomp(string(msg...)))
end
info(msg...; prefix="INFO: ") = info(STDERR, msg..., prefix=prefix)

Expand All @@ -371,7 +376,8 @@ function warn(io::IO, msg...;
(key in have_warned) && return
push!(have_warned, key)
end
print_with_color(warn_color(), io, prefix, str)
print_with_color(warn_color(), io, prefix; bold = true)
print_with_color(warn_color(), io, str)
if bt !== nothing
show_backtrace(io, bt)
end
Expand Down
6 changes: 3 additions & 3 deletions doc/manual/interacting-with-julia.rst
Original file line number Diff line number Diff line change
Expand Up @@ -295,16 +295,16 @@ prompt you can add something like the following to your ``juliarc.jl`` file::

atreplinit(customize_colors)

The available color keys in ``Base.text_colors`` are ``:black``, ``:red``, ``:green``, ``:yellow``,
``:blue``, ``:magenta``, ``:cyan``, ``:white``, ``:normal``, and ``:bold`` as well as the integers 0 to 255 for terminals with 256 color support. Similarly, you can
The available color keys can be seen by typing ``Base.text_colors`` in the help mode of the REPL. Similarly, you can
change the colors for the help and shell prompts and input and answer text by setting the
appropriate field of ``repl`` in the ``customize_colors`` function above (respectively, ``help_color``, ``shell_color``,
``input_color``, and ``answer_color``). For the latter two, be sure that the ``envcolors`` field
is also set to false.

You can also customize the color used to render warning and informational messages by
You can also customize the color used to render error, warning and informational messages by
setting the appropriate environment variable. For instance, to render warning messages in yellow and
informational messages in cyan you can add the following to your ``juliarc.jl`` file::

ENV["JULIA_WARN_COLOR"] = :yellow
ENV["JULIA_INFO_COLOR"] = :cyan
ENV["JULIA_ERROR_COLOR"] = :magenta
Loading

0 comments on commit 39398fd

Please sign in to comment.