Skip to content

Commit

Permalink
use standard repl tooling
Browse files Browse the repository at this point in the history
  • Loading branch information
IanButterworth committed Jun 5, 2024
1 parent f212ff8 commit 2b41b87
Showing 1 changed file with 62 additions and 59 deletions.
121 changes: 62 additions & 59 deletions stdlib/REPL/src/REPL.jl
Original file line number Diff line number Diff line change
Expand Up @@ -575,6 +575,7 @@ mutable struct LineEditREPL <: AbstractREPL
answer_color::String
shell_color::String
help_color::String
pkg_color::String
history_file::Bool
in_shell::Bool
in_help::Bool
Expand All @@ -587,13 +588,13 @@ mutable struct LineEditREPL <: AbstractREPL
interface::ModalInterface
backendref::REPLBackendRef
frontend_task::Task
function LineEditREPL(t,hascolor,prompt_color,input_color,answer_color,shell_color,help_color,history_file,in_shell,in_help,envcolors)
function LineEditREPL(t,hascolor,prompt_color,input_color,answer_color,shell_color,help_color,pkg_color,history_file,in_shell,in_help,envcolors)
opts = Options()
opts.hascolor = hascolor
if !hascolor
opts.beep_colors = [""]
end
new(t,hascolor,prompt_color,input_color,answer_color,shell_color,help_color,history_file,in_shell,
new(t,hascolor,prompt_color,input_color,answer_color,shell_color,help_color,pkg_color,history_file,in_shell,
in_help,envcolors,false,nothing, opts, nothing, Tuple{String,Int}[])
end
end
Expand All @@ -610,6 +611,7 @@ LineEditREPL(t::TextTerminal, hascolor::Bool, envcolors::Bool=false) =
hascolor ? Base.answer_color() : "",
hascolor ? Base.text_colors[:red] : "",
hascolor ? Base.text_colors[:yellow] : "",
hascolor ? Base.text_colors[:blue] : "",
false, false, false, envcolors
)

Expand Down Expand Up @@ -1080,6 +1082,9 @@ setup_interface(
extra_repl_keymap::Any = repl.options.extra_keymap
) = setup_interface(repl, hascolor, extra_repl_keymap)

# TODO: Do this with local state
return_pressed::Bool = false

# This non keyword method can be precompiled which is important
function setup_interface(
repl::LineEditREPL,
Expand Down Expand Up @@ -1155,14 +1160,28 @@ function setup_interface(
end,
sticky = true)

# Set up dummy Pkg mode that will be replaced once Pkg is loaded
# use 6 spaces to occupy the same space as the most likely "@v1.xx" env name
dummy_pkg_mode = Prompt("( ) $PKG_PROMPT",
prompt_prefix = hascolor ? repl.pkg_color : "",
prompt_suffix = hascolor ?
(repl.envcolors ? Base.input_color : repl.input_color) : "",
repl = repl,
complete = replc,
on_done = respond(repl, julia_prompt) do line
# do nothing but remember and get Pkg to execute once switched
end,
sticky = true)


################################# Stage II #############################

# Setup history
# We will have a unified history for all REPL modes
hp = REPLHistoryProvider(Dict{Symbol,Prompt}(:julia => julia_prompt,
:shell => shell_mode,
:help => help_mode))
:help => help_mode,
:pkg => dummy_pkg_mode))
if repl.history_file
try
hist_path = find_hist_file()
Expand All @@ -1185,6 +1204,7 @@ function setup_interface(
julia_prompt.hist = hp
shell_mode.hist = hp
help_mode.hist = hp
dummy_pkg_mode.hist = hp

julia_prompt.on_done = respond(x->Base.parse_input_line(x,filename=repl_filename(repl,hp)), repl, julia_prompt)

Expand Down Expand Up @@ -1225,69 +1245,31 @@ function setup_interface(
end,
']' => function (s::MIState,o...)
if isempty(s) || position(LineEdit.buffer(s)) == 0
# 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
return_pressed = 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
buf = copy(LineEdit.buffer(s))
transition(s, dummy_pkg_mode) do
LineEdit.state(s, dummy_pkg_mode).input_buffer = buf
end
t_replswitch = Threads.@spawn begin
pkgid = Base.PkgId(Base.UUID("44cfe95a-1eb2-52ea-b672-e2afdf69b78f"), "Pkg")
REPLExt = Base.require_stdlib(pkgid, "REPLExt")
if REPLExt isa Module && isdefined(REPLExt, :PkgCompletionProvider)
for mode in repl.interface.modes
if mode isa LineEdit.Prompt && mode.complete isa REPLExt.PkgCompletionProvider
pkg_mode = mode
break
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
if !isempty(s)
@invokelatest(LineEdit.check_for_hint(s)) && @invokelatest(LineEdit.refresh_line(s))
end
if return_pressed # if return was pressed run what was entered
LineEdit.commit_line(s)
terminal = LineEdit.terminal(s)
@invokelatest LineEdit.mode(s).on_done(s, LineEdit.buffer(s), true)
LineEdit.push_undo(s)
if LineEdit.mode(s) === dummy_pkg_mode
if REPLExt isa Module && isdefined(REPLExt, :PkgCompletionProvider)
for mode in repl.interface.modes
if mode isa LineEdit.Prompt && mode.complete isa REPLExt.PkgCompletionProvider
buf = copy(LineEdit.buffer(s))
transition(s, mode) do
LineEdit.state(s, mode).input_buffer = buf
end
if !isempty(s)
@invokelatest(LineEdit.check_for_hint(s)) && @invokelatest(LineEdit.refresh_line(s))
end
break
end
end
transition_finished = true
end
end
REPLExt
end
# while loading just accept all keys, no keymap functionality
# use @async here to keep this on the same thread as the REPL. With @spawn both tasks
# can get stuck waiting for each other
t_stdincopy = @async while !istaskdone(t_replswitch)
try
peek(stdin, Char)
catch
# ignore interrupt
else
@lock iolock begin
if !transition_finished # only take if transition is still pending
c = read(stdin, Char)
if c == '\r' # don't foward return keys
return_pressed = true
else
edit_insert(s, c)
end
end
end
end
end
Base.errormonitor(t_stdincopy)
wait(t_replswitch)
Base.errormonitor(t_replswitch)
else
edit_insert(s, ']')
end
Expand Down Expand Up @@ -1472,7 +1454,28 @@ function setup_interface(

shell_mode.keymap_dict = help_mode.keymap_dict = LineEdit.keymap(b)

allprompts = LineEdit.TextInterface[julia_prompt, shell_mode, help_mode, search_prompt, prefix_prompt]
dummy_pkg_mode.keymap_dict = copy(LineEdit.keymap(b))
dummy_pkg_mode.keymap_dict['\r'] = (s::MIState,o...)->begin
pkgid = Base.PkgId(Base.UUID("44cfe95a-1eb2-52ea-b672-e2afdf69b78f"), "Pkg")
# ok to do this even if Pkg is loading on the other task because of the loading lock
REPLExt = Base.require_stdlib(pkgid, "REPLExt")
if LineEdit.mode(s) === dummy_pkg_mode
if REPLExt isa Module && isdefined(REPLExt, :PkgCompletionProvider)
for mode in repl.interface.modes
if mode isa LineEdit.Prompt && mode.complete isa REPLExt.PkgCompletionProvider
buf = copy(LineEdit.buffer(s))
transition(s, mode) do
LineEdit.state(s, mode).input_buffer = buf
end
break
end
end
end
end
return LineEdit.keymap(b)['\r'](s, o...) # do the normal execute
end

allprompts = LineEdit.TextInterface[julia_prompt, shell_mode, help_mode, dummy_pkg_mode, search_prompt, prefix_prompt]
return ModalInterface(allprompts)
end

Expand Down

0 comments on commit 2b41b87

Please sign in to comment.