From 114e581d3003a51ed39a403046ca3fb80c305d34 Mon Sep 17 00:00:00 2001 From: Simeon Schaub Date: Wed, 8 Jul 2020 07:53:49 +0200 Subject: [PATCH] Fix Ctrl+Q for stack traces (#36344) * alternative fix for #36340 * refactor method hint mechanism * make LAST_SHOWN_LINE_INFOS lowercase * implement style suggestion from @KristofferC * fix use of LAST_SHOWN_LINE_INFOS on latest master --- base/errorshow.jl | 8 +++--- base/methodshow.jl | 17 ++++++++----- stdlib/REPL/src/REPL.jl | 53 ++++++++++++++++++++++------------------ stdlib/REPL/test/repl.jl | 42 +++++++++++++++++++++++++++++++ 4 files changed, 86 insertions(+), 34 deletions(-) diff --git a/base/errorshow.jl b/base/errorshow.jl index 56624c5b39868..5b0c32be6194a 100644 --- a/base/errorshow.jl +++ b/base/errorshow.jl @@ -699,8 +699,8 @@ function print_stackframe(io, i, frame, n, digit_align_width, modulecolor) # Used by the REPL to make it possible to open # the location of a stackframe/method in the editor. - if haskey(io, :LAST_SHOWN_LINE_INFOS) - push!(io[:LAST_SHOWN_LINE_INFOS], (string(frame.file), frame.line)) + if haskey(io, :last_shown_line_infos) + push!(io[:last_shown_line_infos], (string(frame.file), frame.line)) end inlined = getfield(frame, :inlined) @@ -749,8 +749,8 @@ end function show_backtrace(io::IO, t::Vector) - if haskey(io, :LAST_SHOWN_LINE_INFOS) - resize!(io[:LAST_SHOWN_LINE_INFOS], 0) + if haskey(io, :last_shown_line_infos) + empty!(io[:last_shown_line_infos]) end # t is a pre-processed backtrace (ref #12856) diff --git a/base/methodshow.jl b/base/methodshow.jl index ea5f14f9c1ab1..dfe32fd350934 100644 --- a/base/methodshow.jl +++ b/base/methodshow.jl @@ -240,9 +240,10 @@ function show_method_table(io::IO, ms::MethodList, max::Int=-1, header::Bool=tru end n = rest = 0 local last - LAST_SHOWN_LINE_INFOS = get(io, :LAST_SHOWN_LINE_INFOS, Tuple{String,Int}[]) - resize!(LAST_SHOWN_LINE_INFOS, 0) + last_shown_line_infos = get(io, :last_shown_line_infos, nothing) + last_shown_line_infos === nothing || empty!(last_shown_line_infos) + for meth in ms if max==-1 || n true, :module => Main)) - end - - infos = Tuple{String,Int}[] - io = IOContext(io, :LAST_SHOWN_LINE_INFOS => infos) - - show(io, mime, x) - println(io) - - if !isempty(infos) - d.repl.last_shown_line_infos = infos - println( - io, - "\nTo edit a specific method, type the corresponding number into the " * - "REPL and press Ctrl+Q", - ) + with_methodtable_hint(d.repl) do io + get(io, :color, false) && write(io, answer_color(d.repl)) + if isdefined(d.repl, :options) && isdefined(d.repl.options, :iocontext) + # this can override the :limit property set initially + io = foldl(IOContext, d.repl.options.iocontext, + init=IOContext(io, :limit => true, :module => Main)) + end + show(io, mime, x) + println(io) end - nothing end display(d::REPLDisplay, x) = display(d, MIME("text/plain"), x) function print_response(repl::AbstractREPL, @nospecialize(response), show_value::Bool, have_color::Bool) repl.waserror = response[2] - io = IOContext(outstream(repl), :module => Main) - print_response(io, response, show_value, have_color, specialdisplay(repl)) + with_methodtable_hint(repl) do io + io = IOContext(io, :module => Main) + print_response(io, response, show_value, have_color, specialdisplay(repl)) + end nothing end function print_response(errio::IO, @nospecialize(response), show_value::Bool, have_color::Bool, specialdisplay=nothing) @@ -495,6 +484,22 @@ function complete_line(c::LatexCompletions, s) return unique!(map(completion_text, ret)), partial[range], should_complete end +with_methodtable_hint(f, repl) = f(outstream(repl)) +function with_methodtable_hint(f, repl::LineEditREPL) + linfos = Tuple{String,Int}[] + io = IOContext(outstream(repl), :last_shown_line_infos => linfos) + f(io) + if !isempty(linfos) + repl.last_shown_line_infos = linfos + println( + io, + "\nTo edit a specific method, type the corresponding number into the " * + "REPL and press Ctrl+Q", + ) + end + nothing +end + mutable struct REPLHistoryProvider <: HistoryProvider history::Vector{String} history_file::Union{Nothing,IO} diff --git a/stdlib/REPL/test/repl.jl b/stdlib/REPL/test/repl.jl index ba84eb579ec33..ba8a67e911d5f 100644 --- a/stdlib/REPL/test/repl.jl +++ b/stdlib/REPL/test/repl.jl @@ -1240,3 +1240,45 @@ frontend_task = @async begin end REPL.start_repl_backend(backend) Base.wait(frontend_task) + +macro throw_with_linenumbernode(err) + Expr(:block, LineNumberNode(42, Symbol("test.jl")), :(() -> throw($err))) +end + +@testset "last shown line infos" begin + out_stream = IOBuffer() + term = REPL.TTYTerminal("dumb", IOBuffer(), out_stream, IOBuffer()) + repl = REPL.LineEditREPL(term, false) + repl.specialdisplay = REPL.REPLDisplay(repl) + + REPL.print_response(repl, (methods(+), false), true, false) + seekstart(out_stream) + @test count( + contains( + "To edit a specific method, type the corresponding number into the REPL and " * + "press Ctrl+Q" + ), + eachline(out_stream), + ) == 1 + take!(out_stream) + + err = ErrorException("Foo") + bt = try + @throw_with_linenumbernode(err)() + catch + Base.catch_stack() + end + + repl.backendref = REPL.REPLBackendRef(Channel(1), Channel(1)) + put!(repl.backendref.response_channel, (bt, true)) + + REPL.print_response(repl, (err, true), true, false) + seekstart(out_stream) + @test count( + contains( + "To edit a specific method, type the corresponding number into the REPL and " * + "press Ctrl+Q" + ), + eachline(out_stream), + ) == 1 +end