diff --git a/base/REPL.jl b/base/REPL.jl index 0841d7c74ff85..897125e8247af 100644 --- a/base/REPL.jl +++ b/base/REPL.jl @@ -111,15 +111,13 @@ 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: ") - # remove REPL-related frames from interactive printing - eval_ind = findlast(addr->ip_matches_func(addr, :eval), bt) - if eval_ind != 0 - bt = bt[1:eval_ind-1] - end - Base.showerror(IOContext(io, :limit => true), er, bt) + print_with_color(:red, io, "ERROR: ") + # remove REPL-related frames from interactive printing + eval_ind = findlast(addr->Base.REPL.ip_matches_func(addr, :eval), bt) + if eval_ind != 0 + bt = bt[1:eval_ind-1] end + showerror(IOContext(IOContext(io, :hascolor => true), :limit => true), er, bt) end immutable REPLDisplay{R<:AbstractREPL} <: Display @@ -167,6 +165,8 @@ function print_response(errio::IO, val::ANY, bt, show_value::Bool, have_color::B catch err if bt !== nothing println(errio, "SYSTEM: show(lasterr) caused an error") + println(errio, err) + Base.show_backtrace(errio, bt) break end val = err diff --git a/base/client.jl b/base/client.jl index ce83ff0f8ec5d..89c0d2c59e4c8 100644 --- a/base/client.jl +++ b/base/client.jl @@ -14,6 +14,7 @@ const text_colors = AnyDict( :white => "\033[1m\033[37m", :normal => "\033[0m", :bold => "\033[1m", + :nothing => "", ) for i in 0:255 @@ -35,6 +36,7 @@ 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. +Printing with the color `:nothing` will print the string without modifications. """ text_colors @@ -61,6 +63,8 @@ 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)] +stackframe_lineinfo_color() = repl_color("JULIA_STACKFRAME_LINEINFO_COLOR", :bold) +stackframe_function_color() = repl_color("JULIA_STACKFRAME_FUNCTION_COLOR", :bold) function repl_cmd(cmd, out) shell = shell_split(get(ENV,"JULIA_SHELL",get(ENV,"SHELL","/bin/sh"))) diff --git a/base/replutil.jl b/base/replutil.jl index 09c99d1ba9bd4..a2096c6d0c2e4 100644 --- a/base/replutil.jl +++ b/base/replutil.jl @@ -194,9 +194,13 @@ end function showerror(io::IO, ex, bt; backtrace=true) try - showerror(io, ex) + with_output_color(get(io, :hascolor, false) ? :red : :nothing, io) do io + showerror(io, ex) + end finally - backtrace && show_backtrace(io, bt) + if backtrace + show_backtrace(io, bt) + end end end @@ -559,15 +563,22 @@ function show_method_candidates(io::IO, ex::MethodError, kwargs::Vector=Any[]) end end -function show_trace_entry(io, frame, n) +function show_trace_entry(io, frame, n; prefix = " in ") print(io, "\n") - show(io, frame, full_path=true) + show(io, frame, full_path=true; prefix = prefix) n > 1 && print(io, " (repeats ", n, " times)") end function show_backtrace(io::IO, t::Vector) - process_entry(last_frame, n) = - show_trace_entry(io, last_frame, n) + n_frames = 0 + frame_counter = 0 + process_backtrace((a,b) -> n_frames += 1, t) + n_frames != 0 && print(io, "\n\nStacktrace:") + process_entry = (last_frame, n) -> begin + frame_counter += 1 + n_spaces_align = ndigits(n_frames) - ndigits(frame_counter) + 1 + show_trace_entry(io, last_frame, n, prefix = string(" "^n_spaces_align, "[", frame_counter, "] ")) + end process_backtrace(process_entry, t) end diff --git a/base/show.jl b/base/show.jl index e9e5e0a851c95..68aeb30c7a80b 100644 --- a/base/show.jl +++ b/base/show.jl @@ -1030,31 +1030,37 @@ end function show_lambda_types(io::IO, li::Core.MethodInstance) # print a method signature tuple for a lambda definition - if li.specTypes === Tuple - print(io, li.def.name, "(...)") - return - end - - sig = li.specTypes.parameters - ft = sig[1] - if ft <: Function && isempty(ft.parameters) && - isdefined(ft.name.module, ft.name.mt.name) && - ft == typeof(getfield(ft.name.module, ft.name.mt.name)) - print(io, ft.name.mt.name) - elseif isa(ft, DataType) && ft.name === Type.name && isleaftype(ft) - f = ft.parameters[1] - print(io, f) - else - print(io, "(::", ft, ")") + local sig + returned_from_do = false + Base.with_output_color(get(io, :hascolor, false) ? stackframe_function_color() : :nothing, io) do io + if li.specTypes === Tuple + print(io, li.def.name, "(...)") + returned_from_do = true + return + end + sig = li.specTypes.parameters + ft = sig[1] + if ft <: Function && isempty(ft.parameters) && + isdefined(ft.name.module, ft.name.mt.name) && + ft == typeof(getfield(ft.name.module, ft.name.mt.name)) + print(io, ft.name.mt.name) + elseif isa(ft, DataType) && ft.name === Type.name && isleaftype(ft) + f = ft.parameters[1] + print(io, f) + else + print(io, "(::", ft, ")") + end end + returned_from_do && return first = true - print(io, '(') + print_style = get(io, :hascolor, 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(io, ')') + print_with_color(print_style, io, ")") nothing end diff --git a/base/stacktraces.jl b/base/stacktraces.jl index 7f92db5995b0b..dcdc2d38ab2dd 100644 --- a/base/stacktraces.jl +++ b/base/stacktraces.jl @@ -188,7 +188,7 @@ function show_spec_linfo(io::IO, frame::StackFrame) if frame.func === empty_sym @printf(io, "ip:%#x", frame.pointer) else - print(io, frame.func) + print_with_color(get(io, :hascolor, false) ? Base.stackframe_function_color() : :nothing, io, string(frame.func)) end else linfo = get(frame.linfo) @@ -200,16 +200,20 @@ function show_spec_linfo(io::IO, frame::StackFrame) end end -function show(io::IO, frame::StackFrame; full_path::Bool=false) - print(io, " in ") +function show(io::IO, frame::StackFrame; full_path::Bool=false, + prefix = " in ") + print(io, prefix) show_spec_linfo(io, frame) if frame.file !== empty_sym file_info = full_path ? string(frame.file) : basename(string(frame.file)) - print(io, " at ", file_info, ":") - if frame.line >= 0 - print(io, frame.line) - else - print(io, "?") + print(io, " at ") + Base.with_output_color(get(io, :hascolor, false) ? Base.stackframe_lineinfo_color() : :nothing, io) do io + print(io, file_info, ":") + if frame.line >= 0 + print(io, frame.line) + else + print(io, "?") + end end end if frame.inlined diff --git a/base/util.jl b/base/util.jl index be9d904bf0daf..88c622f8fbd7d 100644 --- a/base/util.jl +++ b/base/util.jl @@ -308,7 +308,7 @@ function with_output_color(f::Function, color::Union{Int, Symbol}, io::IO, args. 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 && color != :nothing && print(buf, color_normal) print(io, String(take!(buf))) end end diff --git a/test/cmdlineargs.jl b/test/cmdlineargs.jl index 6dd22d8525c71..cf860bba743d8 100644 --- a/test/cmdlineargs.jl +++ b/test/cmdlineargs.jl @@ -328,12 +328,12 @@ end for precomp in ("yes", "no") bt = readstring(pipeline(ignorestatus(`$(Base.julia_cmd()) --startup-file=no --precompiled=$precomp -E 'include("____nonexistent_file")'`), stderr=catcmd)) - @test contains(bt, "in include_from_node1") + @test contains(bt, "include_from_node1") if is_windows() && Sys.WORD_SIZE == 32 && precomp == "yes" # fixme, issue #17251 - @test_broken contains(bt, "in include_from_node1(::String) at $(joinpath(".","loading.jl"))") + @test_broken contains(bt, "include_from_node1(::String) at $(joinpath(".","loading.jl"))") else - @test contains(bt, "in include_from_node1(::String) at $(joinpath(".","loading.jl"))") + @test contains(bt, "include_from_node1(::String) at $(joinpath(".","loading.jl"))") end lno = match(r"at \.[\/\\]loading\.jl:(\d+)", bt) @test length(lno.captures) == 1 diff --git a/test/compile.jl b/test/compile.jl index 65d4669e9687a..4cd08c1ccba01 100644 --- a/test/compile.jl +++ b/test/compile.jl @@ -209,7 +209,7 @@ try end """) - t = redirected_stderr("ERROR: LoadError: Declaring __precompile__(false) is not allowed in files that are being precompiled.\n in __precompile__") + t = redirected_stderr("ERROR: LoadError: Declaring __precompile__(false) is not allowed in files that are being precompiled.\n\nStacktrace:\n [1] __precompile__") try Base.compilecache("Baz") # from __precompile__(false) error("__precompile__ disabled test failed") @@ -306,7 +306,7 @@ try error("break me") end """) - t = redirected_stderr("ERROR: LoadError: break me\n in error") + t = redirected_stderr("ERROR: LoadError: break me\n\nStacktrace:\n [1] error") try Base.require(:FooBar) error("\"LoadError: break me\" test failed")