Skip to content

Commit

Permalink
Merge pull request #23590 from JuliaLang/rf/repl-redo
Browse files Browse the repository at this point in the history
REPL: enable "redo" after "undo"
  • Loading branch information
rfourquet authored Sep 7, 2017
2 parents 1c0131c + 34df23e commit 1e7cc54
Show file tree
Hide file tree
Showing 2 changed files with 157 additions and 87 deletions.
108 changes: 77 additions & 31 deletions base/repl/LineEdit.jl
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ end
MIState(i, c, a, m) = MIState(i, c, a, m, String[], 0, Char[], 0, :begin)

function show(io::IO, s::MIState)
print(io, "MI State (", s.current_mode, " active)")
print(io, "MI State (", mode(s), " active)")
end

struct InputAreaState
Expand All @@ -68,6 +68,7 @@ mutable struct PromptState <: ModeState
p::Prompt
input_buffer::IOBuffer
undo_buffers::Vector{IOBuffer}
undo_idx::Int
ias::InputAreaState
# indentation of lines which do not include the prompt
# if negative, the width of the prompt is used
Expand Down Expand Up @@ -116,13 +117,13 @@ terminal(s::PromptState) = s.terminal
for f in [:terminal, :on_enter, :add_history, :buffer, :(Base.isempty),
:replace_line, :refresh_multi_line, :input_string, :update_display_buffer,
:empty_undo, :push_undo, :pop_undo]
@eval ($f)(s::MIState, args...) = $(f)(s.mode_state[s.current_mode], args...)
@eval ($f)(s::MIState, args...) = $(f)(state(s), args...)
end

for f in [:edit_insert, :edit_insert_newline, :edit_backspace, :edit_move_left,
:edit_move_right, :edit_move_word_left, :edit_move_word_right]
@eval function ($f)(s::MIState, args...)
$(f)(s.mode_state[s.current_mode], args...)
$(f)(state(s), args...)
return $(Expr(:quote, f))
end
end
Expand Down Expand Up @@ -172,7 +173,7 @@ end

# Prompt Completions
function complete_line(s::MIState)
complete_line(s.mode_state[s.current_mode], s.key_repeats)
complete_line(state(s), s.key_repeats)
refresh_line(s)
:complete_line
end
Expand Down Expand Up @@ -725,10 +726,14 @@ function edit_copy_region(s::MIState)
end

function edit_kill_region(s::MIState)
push_kill!(s, edit_splice!(s)) || return :ignore
push_undo(s)
refresh_line(s)
:edit_kill_region
if push_kill!(s, edit_splice!(s))
refresh_line(s)
:edit_kill_region
else
pop_undo(s)
:ignore
end
end

function edit_transpose_chars(s::MIState)
Expand Down Expand Up @@ -1352,8 +1357,8 @@ function refresh_multi_line(termbuf::TerminalBuffer, s::SearchState)
s.ias = refresh_multi_line(termbuf, s.terminal, buf, s.ias, s.backward ? "(reverse-i-search)`" : "(forward-i-search)`")
end

state(s::MIState, p) = s.mode_state[p]
state(s::PromptState, p) = (@assert s.p == p; s)
state(s::MIState, p=mode(s)) = s.mode_state[p]
state(s::PromptState, p=mode(s)) = (@assert s.p == p; s)
mode(s::MIState) = s.current_mode
mode(s::PromptState) = s.p
mode(s::SearchState) = @assert false
Expand Down Expand Up @@ -1668,7 +1673,8 @@ AnyDict(
# Meta Enter
"\e\r" => (s,o...)->edit_insert_newline(s),
"\e\n" => "\e\r",
"^_" => (s,o...)->(pop_undo(s) ? refresh_line(s) : beep(terminal(s))),
"^_" => (s,o...)->edit_undo!(s),
"\e_" => (s,o...)->edit_redo!(s),
# Simply insert it into the buffer by default
"*" => (s,data,c)->(edit_insert(s, c)),
"^U" => (s,o...)->edit_clear(s),
Expand Down Expand Up @@ -1791,31 +1797,32 @@ function activate(p::TextInterface, s::ModeState, termbuf, term::TextTerminal)
end

function activate(p::TextInterface, s::MIState, termbuf, term::TextTerminal)
@assert p == s.current_mode
activate(p, s.mode_state[s.current_mode], termbuf, term)
@assert p == mode(s)
activate(p, state(s), termbuf, term)
end
activate(m::ModalInterface, s::MIState, termbuf, term::TextTerminal) =
activate(s.current_mode, s, termbuf, term)
activate(mode(s), s, termbuf, term)

commit_changes(t::UnixTerminal, termbuf) = write(t, take!(termbuf.out_stream))
function transition(f::Function, s::MIState, mode)
if mode === :abort

function transition(f::Function, s::MIState, newmode)
if newmode === :abort
s.aborted = true
return
end
if mode === :reset
if newmode === :reset
reset_state(s)
return
end
if !haskey(s.mode_state,mode)
s.mode_state[mode] = init_state(terminal(s), mode)
if !haskey(s.mode_state, newmode)
s.mode_state[newmode] = init_state(terminal(s), newmode)
end
termbuf = TerminalBuffer(IOBuffer())
t = terminal(s)
s.mode_state[s.current_mode] = deactivate(s.current_mode, s.mode_state[s.current_mode], termbuf, t)
s.current_mode = mode
s.mode_state[mode(s)] = deactivate(mode(s), state(s), termbuf, t)
s.current_mode = newmode
f()
activate(mode, s.mode_state[mode], termbuf, t)
activate(newmode, state(s, newmode), termbuf, t)
commit_changes(t, termbuf)
end
transition(s::MIState, mode) = transition((args...)->nothing, s, mode)
Expand All @@ -1830,7 +1837,7 @@ function reset_state(s::PromptState)
end

function reset_state(s::MIState)
for (mode,state) in s.mode_state
for (mode, state) in s.mode_state
reset_state(state)
end
end
Expand All @@ -1855,7 +1862,7 @@ end
run_interface(::Prompt) = nothing

init_state(terminal, prompt::Prompt) =
PromptState(terminal, prompt, IOBuffer(), IOBuffer[], InputAreaState(1, 1),
PromptState(terminal, prompt, IOBuffer(), IOBuffer[], 1, InputAreaState(1, 1),
#=indent(spaces)=# -1)

function init_state(terminal, m::ModalInterface)
Expand All @@ -1878,7 +1885,7 @@ function run_interface(terminal, m::ModalInterface)
Expr(:body,
Expr(:return,
Expr(:call,
QuoteNode(mode(state(s, s.current_mode)).on_done),
QuoteNode(mode(state(s)).on_done),
QuoteNode(s),
QuoteNode(buf),
QuoteNode(ok)))))
Expand All @@ -1894,25 +1901,64 @@ position(s::Union{MIState,ModeState}) = position(buffer(s))

function empty_undo(s::PromptState)
empty!(s.undo_buffers)
s.undo_idx = 1
end

empty_undo(s) = nothing

function push_undo(s::PromptState)
push!(s.undo_buffers, copy(s.input_buffer))
function push_undo(s::PromptState, advance=true)
resize!(s.undo_buffers, s.undo_idx)
s.undo_buffers[end] = copy(s.input_buffer)
advance && (s.undo_idx += 1)
end

push_undo(s) = nothing

# must be called after a push_undo
function pop_undo(s::PromptState)
length(s.undo_buffers) > 0 || return false
s.input_buffer = pop!(s.undo_buffers)
pop!(s.undo_buffers)
s.undo_idx -= 1
end

function edit_undo!(s::MIState)
s.last_action (:edit_redo!, :edit_undo!) && push_undo(s, false)
if edit_undo!(state(s))
:edit_undo!
else
beep(terminal(s))
:ignore
end
end

function edit_undo!(s::PromptState)
s.undo_idx > 1 || return false
s.input_buffer = s.undo_buffers[s.undo_idx -=1]
refresh_line(s)
true
end
edit_undo!(s) = nothing

function edit_redo!(s::MIState)
if s.last_action (:edit_redo!, :edit_undo!) && edit_redo!(state(s))
:edit_redo!
else
beep(terminal(s))
:ignore
end
end

function edit_redo!(s::PromptState)
s.undo_idx < length(s.undo_buffers) || return false
s.input_buffer = s.undo_buffers[s.undo_idx += 1]
refresh_line(s)
true
end
pop_undo(s) = nothing
edit_redo!(s) = nothing

keymap(s::PromptState, prompt::Prompt) = prompt.keymap_dict
keymap_data(s::PromptState, prompt::Prompt) = prompt.keymap_func_data
keymap(ms::MIState, m::ModalInterface) = keymap(ms.mode_state[ms.current_mode], ms.current_mode)
keymap_data(ms::MIState, m::ModalInterface) = keymap_data(ms.mode_state[ms.current_mode], ms.current_mode)
keymap(ms::MIState, m::ModalInterface) = keymap(state(ms), mode(ms))
keymap_data(ms::MIState, m::ModalInterface) = keymap_data(state(ms), mode(ms))

function prompt!(term, prompt, s = init_state(term, prompt))
Base.reseteof(term)
Expand Down
Loading

0 comments on commit 1e7cc54

Please sign in to comment.