From 13635e1321991306aadab3f2eb4fac59ef9b3745 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Tue, 4 Jun 2024 12:23:48 -0400 Subject: [PATCH] Switch to Pkg mode prompt immediately and load Pkg in the background (#54594) Because Pkg is now a pkgimage it can load slowly on slower machines, which is a bit frustrating in the first repl switch. This makes the repl immediately switch to a dummy prompt that looks like Pkg mode to allow the user to keep typing while Pkg loads. During which the keymap is disabled. It works best if julia has >1 thread, otherwise typing stalls during Pkg load. If Pkg takes longer to load than the user to type the command and press return, then the UX isn't great as it won't do anything. https://github.com/JuliaLang/julia/assets/1694067/1bf17323-441a-4db2-8a3b-4d571eac622f --- stdlib/REPL/src/REPL.jl | 42 +++++++++++++++++++++++++---------- stdlib/REPL/src/precompile.jl | 10 +++++++++ 2 files changed, 40 insertions(+), 12 deletions(-) diff --git a/stdlib/REPL/src/REPL.jl b/stdlib/REPL/src/REPL.jl index 2613c54d16427..a39b44d17f9a1 100644 --- a/stdlib/REPL/src/REPL.jl +++ b/stdlib/REPL/src/REPL.jl @@ -1080,9 +1080,6 @@ setup_interface( extra_repl_keymap::Any = repl.options.extra_keymap ) = setup_interface(repl, hascolor, extra_repl_keymap) -# we have to grab this after Pkg is loaded so cache it -pkg_mode::Union{Nothing,LineEdit.Prompt} = nothing - # This non keyword method can be precompiled which is important function setup_interface( repl::LineEditREPL, @@ -1228,11 +1225,17 @@ function setup_interface( end, ']' => function (s::MIState,o...) if isempty(s) || position(LineEdit.buffer(s)) == 0 - global pkg_mode - if pkg_mode === nothing + # print a dummy pkg prompt while Pkg loads + LineEdit.clear_line(LineEdit.terminal(s)) + # use 6 .'s here because its the same width as the most likely `@v1.xx` env name + print(LineEdit.terminal(s), styled"{blue,bold:({gray:......}) pkg> }") + pkg_mode = nothing + transition_finished = false + iolock = Base.ReentrantLock() # to avoid race between tasks reading stdin & input buffer + # spawn Pkg load to avoid blocking typing during loading. Typing will block if only 1 thread + t_replswitch = Threads.@spawn begin pkgid = Base.PkgId(Base.UUID("44cfe95a-1eb2-52ea-b672-e2afdf69b78f"), "Pkg") REPLExt = Base.require_stdlib(pkgid, "REPLExt") - pkg_mode = nothing if REPLExt isa Module && isdefined(REPLExt, :PkgCompletionProvider) for mode in repl.interface.modes if mode isa LineEdit.Prompt && mode.complete isa REPLExt.PkgCompletionProvider @@ -1241,16 +1244,31 @@ function setup_interface( end end end + if pkg_mode !== nothing + @lock iolock begin + buf = copy(LineEdit.buffer(s)) + transition(s, pkg_mode) do + LineEdit.state(s, pkg_mode).input_buffer = buf + end + if !isempty(s) + @invokelatest(LineEdit.check_for_hint(s)) && @invokelatest(LineEdit.refresh_line(s)) + end + transition_finished = true + end + end end - if pkg_mode !== nothing - buf = copy(LineEdit.buffer(s)) - transition(s, pkg_mode) do - LineEdit.state(s, pkg_mode).input_buffer = buf + Base.errormonitor(t_replswitch) + # while loading just accept all keys, no keymap functionality + while !istaskdone(t_replswitch) + # wait but only take if task is still running + peek(stdin, Char) + @lock iolock begin + transition_finished || edit_insert(s, read(stdin, Char)) end - return end + else + edit_insert(s, ']') end - edit_insert(s, ']') end, # Bracketed Paste Mode diff --git a/stdlib/REPL/src/precompile.jl b/stdlib/REPL/src/precompile.jl index 7299742eaef1c..3154642b74307 100644 --- a/stdlib/REPL/src/precompile.jl +++ b/stdlib/REPL/src/precompile.jl @@ -220,4 +220,14 @@ end generate_precompile_statements() precompile(Tuple{typeof(getproperty), REPL.REPLBackend, Symbol}) + +# Helps Pkg repl mode switch +precompile(Tuple{typeof(REPL.Terminals.clear_line), REPL.Terminals.TTYTerminal}) +precompile(Tuple{typeof(Base.print), REPL.Terminals.TTYTerminal, Base.AnnotatedString{String}}) +precompile(Tuple{typeof(Base.peek), Base.TTY, Type{Char}}) +precompile(Tuple{typeof(Base.similar), Array{String, 1}}) +precompile(Tuple{typeof(Base.Iterators.enumerate), Array{String, 1}}) +precompile(Tuple{typeof(Base.setindex!), Array{String, 1}, String, Int64}) +precompile(Tuple{typeof(Base.convert), Type{Base.Dict{String, Union{Array{String, 1}, String}}}, Base.Dict{String, Any}}) + end # Precompile