Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

User-themable stacktraces #51816

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 45 additions & 26 deletions base/errorshow.jl
Original file line number Diff line number Diff line change
Expand Up @@ -100,10 +100,39 @@ function showerror(io::IO, ex, bt; backtrace=true)
end
end

function stacktrace_path(file::Union{Nothing, String}, line::Union{Nothing, Int})
realfile = if !isnothing(file) && file != "" && !startswith(String(file), "REPL")
String(file) |> fixup_stdlib_path |> find_source_file
end
pathstr = file
if !isnothing(pathstr)
stacktrace_expand_basepaths() && (pathstr = something(find_source_file(file), file))
stacktrace_contract_userdir() && (pathstr = contractuser(pathstr))
end
linestr, llen = if !isnothing(line) && line > 0
string(something(pathstr, ""), ':', line), ncodeunits(string(line))
else
something(pathstr, ""), 0
end
if !isnothing(realfile)
flen = ncodeunits(basename(realfile))
AnnotatedString(linestr,
[(1:ncodeunits(linestr), :link => Filesystem.uripath(realfile)),
(1:ncodeunits(linestr)-flen-llen-1, :face => :julia_stacktrace_location),
(ncodeunits(linestr)-flen-llen:ncodeunits(linestr)-llen-1, :face => :julia_stacktrace_filename),
(ncodeunits(linestr)-llen:ncodeunits(linestr), :face => :julia_stacktrace_fileline)])
else
AnnotatedString(linestr, [(1:ncodeunits(linestr), :face => :julia_stacktrace_location)])
end
end

stacktrace_path(location::LineNumberNode) =
stacktrace_path(if !isnothing(location.file) String(location.file) end, location.line)
vtjnash marked this conversation as resolved.
Show resolved Hide resolved

function showerror(io::IO, ex::LoadError, bt; backtrace=true)
!isa(ex.error, LoadError) && print(io, "LoadError: ")
showerror(io, ex.error, bt, backtrace=backtrace)
print(io, "\nin expression starting at $(ex.file):$(ex.line)")
print(io, "\nin expression starting at ", stacktrace_path(ex.file, ex.line))
end
showerror(io::IO, ex::LoadError) = showerror(io, ex, [])

Expand Down Expand Up @@ -616,7 +645,7 @@ end
const update_stackframes_callback = Ref{Function}(identity)

const STACKTRACE_MODULECOLORS = Iterators.Stateful(Iterators.cycle([:magenta, :cyan, :green, :yellow]))
const STACKTRACE_FIXEDCOLORS = IdDict(Base => :light_black, Core => :light_black)
const STACKTRACE_FIXEDCOLORS = IdDict(Base => :julia_stacktrace_basemodule, Core => :julia_stacktrace_basemodule)

function show_full_backtrace(io::IO, trace::Vector; print_linebreaks::Bool)
num_frames = length(trace)
Expand Down Expand Up @@ -701,9 +730,9 @@ function show_reduced_backtrace(io::IO, t::Vector)
cycle_length = repeated_cycle[1][2]
repetitions = repeated_cycle[1][3]
popfirst!(repeated_cycle)
printstyled(io,
"--- the above ", cycle_length, " lines are repeated ",
repetitions, " more time", repetitions>1 ? "s" : "", " ---", color = :light_black)
repmsg = string("--- the above ", cycle_length, " lines are repeated ",
repetitions, " more time", repetitions>1 ? "s" : "", " ---")
print(io, AnnotatedString(repmsg, [(1:ncodeunits(repmsg), :face => :julia_stacktrace_repetition)]))
if i < length(displayed_stackframes)
println(io)
stacktrace_linebreaks() && println(io)
Expand Down Expand Up @@ -756,41 +785,31 @@ function print_stackframe(io, i, frame::StackFrame, n::Int, ndigits_max, modulec
digit_align_width = ndigits_max + 2

# frame number
print(io, " ", lpad("[" * string(i) * "]", digit_align_width))
print(io, " ")
frameindex = lpad('[' * string(i) * ']', digit_align_width)
print(io, ' ', AnnotatedString(frameindex, [(1:ncodeunits(frameindex), :face => :julia_stacktrace_frameindex)]), ' ')

StackTraces.show_spec_linfo(IOContext(io, :backtrace=>true), frame)
if n > 1
printstyled(io, " (repeats $n times)"; color=:light_black)
repmsg = " (repeats $n times)"
print(io, AnnotatedString(repmsg, [(2:ncodeunits(repmsg), :face => :julia_stacktrace_repetition)]))
end
println(io)

# @ Module path / file : line
print_module_path_file(io, modul, file, line; modulecolor, digit_align_width)

# inlined
printstyled(io, inlined ? " [inlined]" : "", color = :light_black)
print(io, if inlined AnnotatedString("[inlined]", [(1:9, :face => :julia_stacktrace_inlined)]) else "" end)
end

function print_module_path_file(io, modul, file, line; modulecolor = :light_black, digit_align_width = 0)
printstyled(io, " " ^ digit_align_width * "@", color = :light_black)

# module
function print_module_path_file(io::IO, modul::Module, file::Union{Nothing, String}, line::Union{Nothing, Int};
modulecolor = :bright_black, digit_align_width = 0)
print(io, ' ' ^ digit_align_width, AnnotatedString("@", [(1:1, :face => :julia_stacktrace_location)]))
if modul !== nothing && modulecolor !== nothing
print(io, " ")
printstyled(io, modul, color = modulecolor)
mstr = string(modul)
print(io, " ", AnnotatedString(mstr, [(1:ncodeunits(mstr), :face => modulecolor)]))
end

# filepath
file = fixup_stdlib_path(file)
stacktrace_expand_basepaths() && (file = something(find_source_file(file), file))
stacktrace_contract_userdir() && (file = contractuser(file))
print(io, " ")
dir = dirname(file)
!isempty(dir) && printstyled(io, dir, Filesystem.path_separator, color = :light_black)

# filename, separator, line
printstyled(io, basename(file), ":", line; color = :light_black, underline = true)
print(io, ' ', stacktrace_path(file, line))
end

function show_backtrace(io::IO, t::Vector)
Expand Down
19 changes: 19 additions & 0 deletions base/path.jl
Original file line number Diff line number Diff line change
Expand Up @@ -613,3 +613,22 @@ relpath(path::AbstractString, startpath::AbstractString) =
for f in (:isdirpath, :splitdir, :splitdrive, :splitext, :normpath, :abspath)
@eval $f(path::AbstractString) = $f(String(path))
end

"""
uripath(path::AbstractString)

Encode `path` as a URI as per RFC1738, RFC3986, and the
[Freedesktop File URI spec](https://www.freedesktop.org/wiki/Specifications/file-uri-spec/).
"""
function uripath(path::String)
vtjnash marked this conversation as resolved.
Show resolved Hide resolved
percent_escape(s) =
'%' * join(map(b -> string(b, base=16), codeunits(s)), '%')
encode_uri_component(s) = replace(
s, r"[^A-Za-z0-9\-_.~]+" => percent_escape)
string("file://", gethostname(), '/',
join(map(encode_uri_component,
split(path, path_separator_re, keepempty=false)),
'/'))
end

uripath(path::AbstractString) = uripath(String(path))