Skip to content

Commit

Permalink
REPL: allow editing current input in editor (via Meta-e) (#33759)
Browse files Browse the repository at this point in the history
  • Loading branch information
rfourquet committed May 12, 2022
1 parent 3653d3d commit 6f4ce97
Show file tree
Hide file tree
Showing 5 changed files with 70 additions and 12 deletions.
3 changes: 3 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,9 @@ Standard library changes

#### REPL

* `Meta-e` now opens the current input in an editor. The content (if modified) will be
executed upon existing the editor.

#### SparseArrays

#### Dates
Expand Down
7 changes: 4 additions & 3 deletions stdlib/REPL/docs/src/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -270,9 +270,10 @@ to do so), or pressing Esc and then the key.
| `meta-l` | Change the next word to lowercase |
| `^/`, `^_` | Undo previous editing action |
| `^Q` | Write a number in REPL and press `^Q` to open editor at corresponding stackframe or method |
| `meta-Left Arrow` | indent the current line on the left |
| `meta-Right Arrow` | indent the current line on the right |
| `meta-.` | insert last word from previous history entry |
| `meta-Left Arrow` | Indent the current line on the left |
| `meta-Right Arrow` | Indent the current line on the right |
| `meta-.` | Insert last word from previous history entry |
| `meta-e` | Edit the current input in an editor |

### Customizing keybindings

Expand Down
46 changes: 46 additions & 0 deletions stdlib/REPL/src/LineEdit.jl
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import ..Terminals: raw!, width, height, cmove, getX,
import Base: ensureroom, show, AnyDict, position
using Base: something

using InteractiveUtils: InteractiveUtils

abstract type TextInterface end # see interface immediately below
abstract type ModeState end # see interface below
abstract type HistoryProvider end
Expand Down Expand Up @@ -1295,6 +1297,49 @@ _edit_indent(buf::IOBuffer, b::Int, num::Int) =
num >= 0 ? edit_splice!(buf, b => b, ' '^num, rigid_mark=false) :
edit_splice!(buf, b => (b - num))

function mode_idx(hist::HistoryProvider, mode::TextInterface)
c = :julia
for (k,v) in hist.mode_mapping
isequal(v, mode) && (c = k)
end
return c
end

function guess_current_mode_name(s)
try
mode_idx(s.current_mode.hist, s.current_mode)
catch
nothing
end
end

# edit current input in editor
function edit_input(s, f = (filename, line) -> InteractiveUtils.edit(filename, line))
mode_name = guess_current_mode_name(s)
filename = tempname()
if mode_name == :julia
filename *= ".jl"
elseif mode_name == :shell
filename *= ".sh"
end
buf = buffer(s)
pos = position(buf)
str = String(take!(buf))
line = 1 + count(==(_newline), view(str, 1:pos))
write(filename, str)
f(filename, line)
str_mod = readchomp(filename)
rm(filename)
if str != str_mod # something was changed, run the input
write(buf, str_mod)
commit_line(s)
:done
else # no change, the edit session probably unsuccessful
write(buf, str)
seek(buf, pos) # restore state from before edit
refresh_line(s)
end
end

history_prev(::EmptyHistoryProvider) = ("", false)
history_next(::EmptyHistoryProvider) = ("", false)
Expand Down Expand Up @@ -2337,6 +2382,7 @@ AnyDict(
"\eu" => (s::MIState,o...)->edit_upper_case(s),
"\el" => (s::MIState,o...)->edit_lower_case(s),
"\ec" => (s::MIState,o...)->edit_title_case(s),
"\ee" => (s::MIState,o...) -> edit_input(s),
)

const history_keymap = AnyDict(
Expand Down
11 changes: 2 additions & 9 deletions stdlib/REPL/src/REPL.jl
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ import ..LineEdit:
terminal,
MIState,
PromptState,
TextInterface
TextInterface,
mode_idx

include("REPLCompletions.jl")
using .REPLCompletions
Expand Down Expand Up @@ -601,14 +602,6 @@ function hist_from_file(hp::REPLHistoryProvider, path::String)
return hp
end

function mode_idx(hist::REPLHistoryProvider, mode::TextInterface)
c = :julia
for (k,v) in hist.mode_mapping
isequal(v, mode) && (c = k)
end
return c
end

function add_history(hist::REPLHistoryProvider, s::PromptState)
str = rstrip(String(take!(copy(s.input_buffer))))
isempty(strip(str)) && return
Expand Down
15 changes: 15 additions & 0 deletions stdlib/REPL/test/repl.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1499,3 +1499,18 @@ for prompt = ["TestΠ", () -> randstring(rand(1:10))]
@test buffercontents(LineEdit.buffer(s)) == "xyz = 2"
end
end

fake_repl() do stdin_write, stdout_read, repl
repltask = @async begin
REPL.run_repl(repl)
end
repl.interface = REPL.setup_interface(repl)
s = LineEdit.init_state(repl.t, repl.interface)
LineEdit.edit_insert(s, "1234")
@show buffercontents(LineEdit.buffer(s))
input_f = function(filename, line)
write(filename, "123456\n")
end
LineEdit.edit_input(s, input_f)
@test buffercontents(LineEdit.buffer(s)) == "123456"
end

0 comments on commit 6f4ce97

Please sign in to comment.