Skip to content

Commit

Permalink
REPL: jump to first/last history entries (#22829)
Browse files Browse the repository at this point in the history
  • Loading branch information
rfourquet authored Jul 27, 2017
1 parent 36f2bd0 commit 29c78b7
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 41 deletions.
2 changes: 2 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -652,6 +652,8 @@ Library improvements
`enumerate(IndexLinear, iterable)` yields linear indices and
`enumerate(IndexCartesian, iterable)` yields cartesian indices ([#16378]).
* Jump to first/last history entries in the REPL via "Alt-<" and "Alt->" ([#22829]).
Compiler/Runtime improvements
-----------------------------
Expand Down
6 changes: 5 additions & 1 deletion base/repl/LineEdit.jl
Original file line number Diff line number Diff line change
Expand Up @@ -588,6 +588,8 @@ end

history_prev(::EmptyHistoryProvider) = ("", false)
history_next(::EmptyHistoryProvider) = ("", false)
history_first(::EmptyHistoryProvider) = ("", false)
history_last(::EmptyHistoryProvider) = ("", false)
history_search(::EmptyHistoryProvider, args...) = false
add_history(::EmptyHistoryProvider, s) = nothing
add_history(s::PromptState) = add_history(mode(s).hist, s)
Expand Down Expand Up @@ -1443,7 +1445,9 @@ const history_keymap = AnyDict(
# Page Up
"\e[5~" => (s,o...)->(history_prev(s, mode(s).hist)),
# Page Down
"\e[6~" => (s,o...)->(history_next(s, mode(s).hist))
"\e[6~" => (s,o...)->(history_next(s, mode(s).hist)),
"\e<" => (s,o...)->(history_first(s, mode(s).hist)),
"\e>" => (s,o...)->(history_last(s, mode(s).hist)),
)

const prefix_history_keymap = merge!(
Expand Down
28 changes: 20 additions & 8 deletions base/repl/REPL.jl
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ import ..LineEdit:
history_next_prefix,
history_prev,
history_prev_prefix,
history_first,
history_last,
history_search,
accept_result,
terminal
Expand Down Expand Up @@ -463,44 +465,54 @@ function LineEdit.accept_result(s, p::LineEdit.HistoryPrompt{REPLHistoryProvider
end

function history_prev(s::LineEdit.MIState, hist::REPLHistoryProvider,
save_idx::Int = hist.cur_idx)
num::Int=1, save_idx::Int = hist.cur_idx)
num <= 0 && return history_next(s, hist, -num, save_idx)
hist.last_idx = -1
m = history_move(s, hist, hist.cur_idx-1, save_idx)
m = history_move(s, hist, hist.cur_idx-num, save_idx)
if m === :ok
LineEdit.move_input_start(s)
LineEdit.reset_key_repeats(s) do
LineEdit.move_line_end(s)
end
LineEdit.refresh_line(s)
elseif m === :skip
hist.cur_idx -= 1
history_prev(s, hist, save_idx)
history_prev(s, hist, num+1, save_idx)
else
Terminals.beep(LineEdit.terminal(s))
end
end

function history_next(s::LineEdit.MIState, hist::REPLHistoryProvider,
save_idx::Int = hist.cur_idx)
num::Int=1, save_idx::Int = hist.cur_idx)
if num == 0
Terminals.beep(LineEdit.terminal(s))
return
end
num < 0 && return history_prev(s, hist, -num, save_idx)
cur_idx = hist.cur_idx
max_idx = length(hist.history) + 1
if cur_idx == max_idx && 0 < hist.last_idx
# issue #6312
cur_idx = hist.last_idx
hist.last_idx = -1
end
m = history_move(s, hist, cur_idx+1, save_idx)
m = history_move(s, hist, cur_idx+num, save_idx)
if m === :ok
LineEdit.move_input_end(s)
LineEdit.refresh_line(s)
elseif m === :skip
hist.cur_idx += 1
history_next(s, hist, save_idx)
history_next(s, hist, num+1, save_idx)
else
Terminals.beep(LineEdit.terminal(s))
end
end

history_first(s::LineEdit.MIState, hist::REPLHistoryProvider) =
history_prev(s, hist, hist.cur_idx - 1)

history_last(s::LineEdit.MIState, hist::REPLHistoryProvider) =
history_next(s, hist, length(hist.history) - hist.cur_idx + 1)

function history_move_prefix(s::LineEdit.PrefixSearchState,
hist::REPLHistoryProvider,
prefix::AbstractString,
Expand Down
66 changes: 34 additions & 32 deletions doc/src/manual/interacting-with-julia.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,38 +139,40 @@ control-key, there are also meta-key bindings. These vary more by platform, but
default to using alt- or option- held down with a key to send the meta-key (or can be configured
to do so).

| Keybinding | Description |
|:------------------- |:-------------------------------------------------------------------------------- |
| **Program control** |   |
| `^D` | Exit (when buffer is empty) |
| `^C` | Interrupt or cancel |
| `^L` | Clear console screen |
| Return/Enter, `^J` | New line, executing if it is complete |
| meta-Return/Enter | Insert new line without executing it |
| `?` or `;` | Enter help or shell mode (when at start of a line) |
| `^R`, `^S` | Incremental history search, described above |
| **Cursor movement** |   |
| Right arrow, `^F` | Move right one character |
| Left arrow, `^B` | Move left one character |
| Home, `^A` | Move to beginning of line |
| End, `^E` | Move to end of line |
| `^P` | Change to the previous or next history entry |
| `^N` | Change to the next history entry |
| Up arrow | Move up one line (or to the previous history entry) |
| Down arrow | Move down one line (or to the next history entry) |
| Page-up | Change to the previous history entry that matches the text before the cursor |
| Page-down | Change to the next history entry that matches the text before the cursor |
| `meta-F` | Move right one word |
| `meta-B` | Move left one word |
| **Editing** |   |
| Backspace, `^H` | Delete the previous character |
| Delete, `^D` | Forward delete one character (when buffer has text) |
| meta-Backspace | Delete the previous word |
| `meta-D` | Forward delete the next word |
| `^W` | Delete previous text up to the nearest whitespace |
| `^K` | "Kill" to end of line, placing the text in a buffer |
| `^Y` | "Yank" insert the text from the kill buffer |
| `^T` | Transpose the characters about the cursor |
| Keybinding | Description |
|:------------------- |:------------------------------------------------------------------------------------------ |
| **Program control** |   |
| `^D` | Exit (when buffer is empty) |
| `^C` | Interrupt or cancel |
| `^L` | Clear console screen |
| Return/Enter, `^J` | New line, executing if it is complete |
| meta-Return/Enter | Insert new line without executing it |
| `?` or `;` | Enter help or shell mode (when at start of a line) |
| `^R`, `^S` | Incremental history search, described above |
| **Cursor movement** |   |
| Right arrow, `^F` | Move right one character |
| Left arrow, `^B` | Move left one character |
| Home, `^A` | Move to beginning of line |
| End, `^E` | Move to end of line |
| `^P` | Change to the previous or next history entry |
| `^N` | Change to the next history entry |
| Up arrow | Move up one line (or to the previous history entry) |
| Down arrow | Move down one line (or to the next history entry) |
| Page-up | Change to the previous history entry that matches the text before the cursor |
| Page-down | Change to the next history entry that matches the text before the cursor |
| `meta-F` | Move right one word |
| `meta-B` | Move left one word |
| `meta-<` | Change to the first history entry |
| `meta->` | Change to the last history entry |
| **Editing** |   |
| Backspace, `^H` | Delete the previous character |
| Delete, `^D` | Forward delete one character (when buffer has text) |
| meta-Backspace | Delete the previous word |
| `meta-D` | Forward delete the next word |
| `^W` | Delete previous text up to the nearest whitespace |
| `^K` | "Kill" to end of line, placing the text in a buffer |
| `^Y` | "Yank" insert the text from the kill buffer |
| `^T` | Transpose the characters about the cursor |
| `^Q` | Write a number in REPL and press `^Q` to open editor at corresponding stackframe or method |


Expand Down
20 changes: 20 additions & 0 deletions test/repl.jl
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,26 @@ begin
@test LineEdit.mode(s) == repl_mode
@test buffercontents(LineEdit.buffer(s)) == "wip"
@test position(LineEdit.buffer(s)) == 3
LineEdit.history_next(s, hp)
@test buffercontents(LineEdit.buffer(s)) == "wip"
LineEdit.history_prev(s, hp, 2)
@test LineEdit.mode(s) == shell_mode
@test buffercontents(LineEdit.buffer(s)) == "ls"
LineEdit.history_prev(s, hp, -2) # equivalent to history_next(s, hp, 2)
@test LineEdit.mode(s) == repl_mode
@test buffercontents(LineEdit.buffer(s)) == "2 + 2"
LineEdit.history_next(s, hp, -2) # equivalent to history_prev(s, hp, 2)
@test LineEdit.mode(s) == shell_mode
@test buffercontents(LineEdit.buffer(s)) == "ls"
LineEdit.history_first(s, hp)
@test LineEdit.mode(s) == repl_mode
@test buffercontents(LineEdit.buffer(s)) == "é"
LineEdit.history_next(s, hp, 6)
@test LineEdit.mode(s) == shell_mode
@test buffercontents(LineEdit.buffer(s)) == "ls"
LineEdit.history_last(s, hp)
@test buffercontents(LineEdit.buffer(s)) == "wip"
@test position(LineEdit.buffer(s)) == 3
LineEdit.move_line_start(s)
@test position(LineEdit.buffer(s)) == 0

Expand Down

0 comments on commit 29c78b7

Please sign in to comment.