Skip to content

Commit

Permalink
add inline repl tab complete hints
Browse files Browse the repository at this point in the history
  • Loading branch information
IanButterworth committed Sep 8, 2023
1 parent de297fa commit 72dda46
Showing 1 changed file with 42 additions and 5 deletions.
47 changes: 42 additions & 5 deletions stdlib/REPL/src/LineEdit.jl
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ mutable struct PromptState <: ModeState
p::Prompt
input_buffer::IOBuffer
region_active::Symbol # :shift or :mark or :off
hint::Union{String,Nothing}
undo_buffers::Vector{IOBuffer}
undo_idx::Int
ias::InputAreaState
Expand Down Expand Up @@ -361,7 +362,7 @@ function show_completions(s::PromptState, completions::Vector{String})
end
end

# Prompt Completions
# Prompt Completions & Hints
function complete_line(s::MIState)
set_action!(s, :complete_line)
if complete_line(state(s), s.key_repeats, s.active_module)
Expand All @@ -372,6 +373,27 @@ function complete_line(s::MIState)
end
end

function check_for_hint(s::MIState)
st = state(s)
completions, partial, _ = complete_line(st.p.complete, st, s.active_module)::Tuple{Vector{String},String,Bool}
if length(completions) == 1
st.hint = completions[1][sizeof(partial)+1:end]
return refresh_line(s)
elseif length(completions) > 1
p = common_prefix(completions)
if p in completions # i.e. complete `@time` even though `@time_imports` etc. exists
st.hint = p[sizeof(partial)+1:end]
return refresh_line(s)
end
end
if !isnothing(st.hint)
st.hint = "" # don't set to nothing here. That will be done in `maybe_show_hint`
return refresh_line(s)
else
return nothing
end
end

function complete_line(s::PromptState, repeats::Int, mod::Module)
completions, partial, should_complete = complete_line(s.p.complete, s, mod)::Tuple{Vector{String},String,Bool}
isempty(completions) && return false
Expand Down Expand Up @@ -432,12 +454,27 @@ prompt_string(p::Prompt) = prompt_string(p.prompt)
prompt_string(s::AbstractString) = s
prompt_string(f::Function) = Base.invokelatest(f)

function maybe_show_hint(s::PromptState)
isa(s.hint, String) || return nothing
if isempty(s.hint)
print(terminal(s), "\e[0K")
s.hint = nothing
else
Base.printstyled(terminal(s), s.hint, color=:light_black)
cmove_left(terminal(s), textwidth(s.hint))
s.hint = "" # clear line remainder of line but only once after a hint to allow column movement
end
return nothing
end

function refresh_multi_line(s::PromptState; kw...)
if s.refresh_wait !== nothing
close(s.refresh_wait)
s.refresh_wait = nothing
end
refresh_multi_line(terminal(s), s; kw...)
r = refresh_multi_line(terminal(s), s; kw...)
maybe_show_hint(s)
return r
end
refresh_multi_line(s::ModeState; kw...) = refresh_multi_line(terminal(s), s; kw...)
refresh_multi_line(termbuf::TerminalBuffer, s::ModeState; kw...) = refresh_multi_line(termbuf, terminal(s), s; kw...)
Expand Down Expand Up @@ -2424,8 +2461,8 @@ AnyDict(
"\e\n" => "\e\r",
"^_" => (s::MIState,o...)->edit_undo!(s),
"\e_" => (s::MIState,o...)->edit_redo!(s),
# Simply insert it into the buffer by default
"*" => (s::MIState,data,c::StringLike)->(edit_insert(s, c)),
# Show hints at what tab complete would do by default
"*" => (s::MIState,data,c::StringLike)->(edit_insert(s, c); check_for_hint(s)),
"^U" => (s::MIState,o...)->edit_kill_line_backwards(s),
"^K" => (s::MIState,o...)->edit_kill_line_forwards(s),
"^Y" => (s::MIState,o...)->edit_yank(s),
Expand Down Expand Up @@ -2634,7 +2671,7 @@ end
run_interface(::Prompt) = nothing

init_state(terminal, prompt::Prompt) =
PromptState(terminal, prompt, IOBuffer(), :off, IOBuffer[], 1, InputAreaState(1, 1),
PromptState(terminal, prompt, IOBuffer(), :off, nothing, IOBuffer[], 1, InputAreaState(1, 1),
#=indent(spaces)=# -1, Threads.SpinLock(), 0.0, -Inf, nothing)

function init_state(terminal, m::ModalInterface)
Expand Down

0 comments on commit 72dda46

Please sign in to comment.