Skip to content

Commit

Permalink
Link renderable development (#180)
Browse files Browse the repository at this point in the history
* bump

* bump

* working on making Link work

* functional link?

* fixing them errors

* fixed behavior of hidden frames in stacktrace and docs

* fixed bugs

* bugs fixed and more test

* bugfix

* tests fix

* tests fixing

* tests fix

* tests fix
  • Loading branch information
FedeClaudi authored Dec 11, 2022
1 parent ccd11be commit dbddbea
Show file tree
Hide file tree
Showing 154 changed files with 1,152 additions and 473 deletions.
1 change: 1 addition & 0 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ Markdown = "d6f4376e-aef5-505a-96c1-9c027394607a"
MyterialColors = "1c23619d-4212-4747-83aa-717207fae70f"
OrderedCollections = "bac558e1-5e72-5ebc-8fee-abe8a469f55d"
Parameters = "d96e819e-fc66-5662-9728-84c9c7592b0a"
Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
ProgressLogging = "33c8b6b6-d38a-422a-b730-caa89a2f386c"
SnoopPrecompile = "66db9d55-30c0-4569-8b51-7e840670fc0c"
Tables = "bd369af6-aec1-5ad0-b16a-f7cc5008161c"
Expand Down
22 changes: 18 additions & 4 deletions docs/src/adv/errors_tracebacks.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,26 @@ test()
![](stacktrace.png)


!!! warning "Term is opinionated"
In altering Julia's default stacktraces handling, a few choices where made such as: inveriting the order in which the backtrace's stack frames are shown and hiding frames from `Base` or other packages installed through `Pkg` (similarly to [AbbreviatedStackTraces.jl](https://github.com/BioTurboNick/AbbreviatedStackTraces.jl)).
When installing `Term`'s stacktrace system with `install_term_stacktrace`, you can use the keyword arguments to alter this behavior
### Term is opinionated

In altering Julia's default stacktraces handling, a few choices where made such as: inveriting the order in which the backtrace's stack frames are shown and hiding frames from `Base` or other packages installed through `Pkg` (similarly to [AbbreviatedStackTraces.jl](https://github.com/BioTurboNick/AbbreviatedStackTraces.jl)).
When installing `Term`'s stacktrace system with `install_term_stacktrace`, you can use the keyword arguments to alter this behavior

```@example
using Term # hide
install_term_repr() # hide
install_term_stacktrace
install_term_stacktrace(
reverse_backtrace::Bool = true, # change me!
max_n_frames::Int = 30,
hide_frames = true,
)
```

but you can also do more, if you just want to quickly change some options (e.g. to deal with a particularly though bug). You can set flags to change the behavior on the fly:

```@example
import Term: STACKTRACE_HIDE_MODULES, STACKTRACE_HIDE_FRAME
STACKTRACE_HIDE_MODULES[] = ["REPL", "OhMyREPL"] # list names of modules you want ignored in the stacktrace
STACKTRACE_HIDE_FRAME[] = false # set to true to hide frame, false to show all of them
```
2 changes: 1 addition & 1 deletion scripts/panel.jl
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ clear()

pprint(pan) = begin
print(" " * hLine(pan.measure.w; style = "red"))
print(vLine(pan.measure.h + 1; style = "red") * pan)
print(vLine(pan.measure.h; style = "red") * pan)
println(pan.measure, " ", length(pan.segments))
# print(pan)
end
Expand Down
6 changes: 6 additions & 0 deletions src/Term.jl
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ module Term

using Unicode

const STACKTRACE_HIDDEN_MODULES = Ref(String[])
const STACKTRACE_HIDE_FRAME = Ref(true)

const DEBUG_ON = Ref(false)

const ACTIVE_CONSOLE_WIDTH = Ref{Union{Nothing,Int}}(nothing)
Expand Down Expand Up @@ -69,6 +72,7 @@ include("boxes.jl")
include("console.jl")
include("renderables.jl")
include("layout.jl")
include("link.jl")
include("panels.jl")
include("errors.jl")
include("tprint.jl")
Expand Down Expand Up @@ -116,6 +120,8 @@ using .Renderables: AbstractRenderable, Renderable, RenderableText

using .Layout

using .Links

using .Panels: Panel, TextBox, @nested_panels

# define additional methods for measure functions
Expand Down
27 changes: 27 additions & 0 deletions src/__text_utils.jl
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ This regex uses lookahead and lookbehind to exclude {{
at the beginning of a tag, with this:
(?<!\\{)\\[(?!\\{)
"""
RECURSIVE_OPEN_TAG_REGEX = r"\{(?:[^{}]*){2,}\}"
OPEN_TAG_REGEX = r"(?<!\{)\{(?!\{)[a-zA-Z _0-9. ,()#\n]*\}"
CLOSE_TAG_REGEX = r"\{\/[a-zA-Z _0-9. ,()#\n]+[^/\{]\}"
GENERIC_CLOSER_REGEX = r"(?<!\{)\{(?!\{)\/\}"
Expand Down Expand Up @@ -366,3 +367,29 @@ function str_trunc(
out[end] != ' ' && (out *= trailing_dots)
return out
end

# ---------------------------------------------------------------------------- #
# LINK #
# ---------------------------------------------------------------------------- #

"""
excise_link_display_text(link::String)
Given a link string of the form:
"\x1b]8;;LINK_DESTINATION\x1b\\LINK_DISPLAY_TEXT\x1b]8;;\x1b\\"
this function returns "LINK_DISPLAY_TEXT" alone.
"""
function excise_link_display_text(link::AbstractString)
parts = split(link, "\x1b\\")
return if length(parts) > 1
replace(parts[2], "\e]8;;" => "")
else
""
end
end

"""
string_type(x)
Return the type of `x` if it's an AbstractString, else String
"""
string_type(x) = x isa AbstractString ? typeof(x) : String
147 changes: 84 additions & 63 deletions src/_errors.jl
Original file line number Diff line number Diff line change
@@ -1,11 +1,24 @@
import Base.StackTraces: StackFrame
import MyterialColors: pink, indigo_light
import Term: read_file_lines
using Pkg

# ---------------------------------------------------------------------------- #
# MISC #
# ---------------------------------------------------------------------------- #

function get_frame_file(frame::StackFrame)
file = string(frame.file)
file = Base.fixup_stdlib_path(file)
Base.stacktrace_expand_basepaths() && (file = something(find_source_file(file), file))
Base.stacktrace_contract_userdir() && (file = Base.contractuser(file))

return if isnothing(file)
""
else
basename(file)
end
end
"""
function frame_module end
Expand All @@ -23,6 +36,7 @@ function frame_module(frame::StackFrame)::Union{Nothing,String}
end
m = !isnothing(m) ? string(m) : frame_module(string(frame.file))

isnothing(m) && return frame_module(get_frame_file(frame))
return m
end

Expand All @@ -42,18 +56,28 @@ frame_module(iip::Base.InterpreterIP) = string(iip.mod)
A frame should skip if it's in Base or an installed package.
"""
should_skip(frame::StackFrame) =
frame_module(frame) ["Base", "Main", nothing] || (
contains(string(frame.file), r"[/\\].julia[/\\]") ||
contains(string(frame.file), r"julia[/\\]stdlib") ||
contains(string(frame.file), r"julia[/\\]lib") ||
contains(string(frame.file), r"julialang.language")
function should_skip(frame::StackFrame, modul)
mod = something(modul, frame)
f = get_frame_file(frame)

bad = mod ["Base", "Main", nothing, "VSCodeServer", "REPL"]
bad = bad || mod STACKTRACE_HIDDEN_MODULES[]

return bad || (
contains(f, r"[/\\].julia[/\\]") ||
contains(f, r"julia[/\\]stdlib") ||
contains(f, r"julia[/\\]lib") ||
contains(f, "julialang.language") ||
contains(f, "libjulia")
)
should_skip(frame::StackFrame, hide::Bool) = hide ? should_skip(frame) : false
should_skip(pointer::Ptr) = should_skip(StackTraces.lookup(pointer)[1])
should_skip(pointer::Ptr, hide::Bool) = hide ? should_skip(pointer) : false
should_skip(iip::Base.InterpreterIP) = true
should_skip(iip::Base.InterpreterIP, hide::Bool) = true
end
should_skip(frame::StackFrame, hide::Bool, args...) =
hide ? should_skip(frame, args...) : false
should_skip(pointer::Ptr, args...) = should_skip(StackTraces.lookup(pointer)[1], args...)
should_skip(pointer::Ptr, hide::Bool, args...) =
hide ? should_skip(pointer, args...) : false
should_skip(iip::Base.InterpreterIP, args...) = true
should_skip(iip::Base.InterpreterIP, hide::Bool, args...) = true

"""
parse_kw_func_name(frame::StackFrame)
Expand Down Expand Up @@ -103,21 +127,18 @@ function get_frame_function_name(frame::StackFrame, ctx::StacktraceContext)
(func = parse_kw_func_name(frame))

# format function name
func =
replace(
func,
r"(?<group>^[^(]+)" => SubstitutionString(
"{$(ctx.theme.func)}" * s"\g<0>" * "{/$(ctx.theme.func)}",
),
) |>
highlight |>
apply_style
func = replace(
func,
r"(?<group>^[^(]+)" => SubstitutionString(
"{$(ctx.theme.func)}" * s"\g<0>" * "{/$(ctx.theme.func)}",
),
)

func = highlight(func) |> apply_style
func = replace(func, RECURSIVE_OPEN_TAG_REGEX => "")

# reshape but taking care of potential curly bracktes
func =
reshape_text(escape_brackets(func), ctx.func_name_w; ignore_markup = true) |>
unescape_brackets
func = do_by_line(remove_markup, func)
func = reshape_text(func, ctx.func_name_w; ignore_markup = true)
return RenderableText(func)
end

Expand Down Expand Up @@ -193,16 +214,10 @@ function add_stack_frame!(

# make file line & load source code around error and render it
panel_content = if length(string(frame.file)) > 0
file = Base.fixup_stdlib_path(string(frame.file))
Base.stacktrace_expand_basepaths() &&
(file = something(Base.find_source_file(file), file))
Base.stacktrace_contract_userdir() && (file = Base.contractuser(file))
file_line = RenderableText(
"{dim}$(file):{bold $(ctx.theme.text_accent)}$(frame.line){/bold $(ctx.theme.text_accent)}{/dim}";
width = ctx.func_name_w,
)
# get a link renderable pointing to error
source_file = Link(string(frame.file), frame.line; style = "underline dim")
_out = func_line / source_file

_out = func_line / file_line
error_source = render_error_code_line(ctx, frame; δ = δ)
isnothing(error_source) || (_out /= error_source)
_out
Expand All @@ -221,7 +236,7 @@ function add_stack_frame!(
kwargs...,
)
else
pad(" " * func_line; width = ctx.frame_panel_w, method = :right)
pad(" " * panel_content; width = ctx.frame_panel_w, method = :right)
end

numren = vertical_pad("{dim}($num){/dim} ", height(panel), :center)
Expand Down Expand Up @@ -274,7 +289,7 @@ function add_number_frames_skipped!(
modules = join(unique(string.(filter(!isnothing, skipped_frames_modules))), ", ")
modules = filter(x -> x != "nothing", modules)
in_mod = length(modules) == 0 ? "" : "in {$accent}$modules{/$accent}"
word = plural("frame", length(modules))
word = n_skipped > 1 ? "frames" : "frame"

# render
push!(
Expand Down Expand Up @@ -315,7 +330,6 @@ function render_backtrace(

# get the module each frame's code line is defined in
frames_modules = frame_module.(bt)
# println.(zip(frames_modules, should_skip.(bt)))

"""
Define a few variables to keep track of during stack
Expand All @@ -334,16 +348,14 @@ function render_backtrace(

# render each frame
content = AbstractRenderable[]
curr_module = nothing
for (num, frame) in enumerate(bt)
# if the current frame's module differs from the previous one, show module name
curr_module = frames_modules[num]
(
curr_module != prev_frame_module &&
!should_skip(frame, hide_frames) &&
!isnothing(curr_module)
) && add_new_module_name!(content, ctx, curr_module)

# render frame
curr_module =
(num > 1 && !isnothing(curr_module)) ?
something(frames_modules[num], curr_module) : frames_modules[num]

# get params for rendering
frame_panel_kwargs = if num == 1 # first frame is highlighted
Dict(
:subtitle => reverse_backtrace ? "TOP LEVEL" : "ERROR LINE",
Expand All @@ -367,18 +379,13 @@ function render_backtrace(
Dict(:style => "hidden")
end
δ = num in (1, length(bt)) ? 2 : 0
(should_skip(frame, hide_frames) && num [1, length(bt)]) || add_stack_frame!(
content,
frame,
ctx,
num [1, length(bt)],
num;
δ = δ,
frame_panel_kwargs...,
)
to_skip =
should_skip(frame, hide_frames, curr_module) &&
num [1, length(bt)] &&
STACKTRACE_HIDE_FRAME[]

# keep track of frames being skipped
if num != 1 || num != length(bt)
if num [1, length(bt)]
# skip extra panels for long stack traces
if tot_frames_added > max_n_frames &&
num < length(bt) - 5 &&
Expand All @@ -390,10 +397,8 @@ function render_backtrace(
)
push!(content, skipped_line)
added_skipped_message = true
else # show "inner" frames without additional info, hide base optionally
# skip frames in modules like Base
to_skip = should_skip(frame, hide_frames)

else
# show number of frames skipped
if (to_skip == false || num == length(bt) - 1) && n_skipped > 0
add_number_frames_skipped!(
Expand All @@ -408,20 +413,36 @@ function render_backtrace(
end

# skip
to_skip && begin
if to_skip
# @info "frame" num to_skip n_skipped curr_module frames_modules[num]
n_skipped += 1
push!(skipped_frames_modules, curr_module)
push!(skipped_frames_modules, frames_modules[num])
continue
else
# @info "frame" num to_skip n_skipped curr_module
n_skipped, skipped_frames_modules = 0, []
tot_frames_added += 1
end

# show
n_skipped, skipped_frames_modules = 0, []
tot_frames_added += 1
end
else
tot_frames_added += 1
end

# mark switch to a new module
(curr_module != prev_frame_module && !to_skip && !isnothing(curr_module)) &&
add_new_module_name!(content, ctx, curr_module)

# add frame if not hidden
to_skip || add_stack_frame!(
content,
frame,
ctx,
num [1, length(bt)],
num;
δ = δ,
frame_panel_kwargs...,
)

isnothing(curr_module) || (prev_frame_module = curr_module)
end

Expand Down
11 changes: 6 additions & 5 deletions src/_tables.jl
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ function calc_columns_widths(
N_rows::Int,
columns_widths::Union{Nothing,Vector,Int},
show_header::Bool,
header,
tb,
sch,
footer,
hpad,
header::Union{Tuple,AbstractVector,String,Nothing},
tb::TablesPkg.AbstractColumns,
sch, # table schema
footer::Union{Tuple,AbstractVector,String,Nothing},
hpad::Union{Nothing,Int,AbstractVector},
)
if !isnothing(columns_widths)
columns_widths = expand(columns_widths, N_cols)
Expand All @@ -27,6 +27,7 @@ function calc_columns_widths(
data_widths = collect(map(c -> max(width.(tb[c])...), sch.names))
footers_widths = isnothing(footer) ? zeros(N_cols) : collect(width.(footer))
widths = hcat(headers_widths, data_widths, footers_widths)

hpad = isa(hpad, Int) ? fill(hpad, N_rows) : hpad
widths = Int.([mapslices(x -> max(x...), widths; dims = 2)...] .+ hpad * 2)
return widths
Expand Down
Loading

0 comments on commit dbddbea

Please sign in to comment.