Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make vim bindings opt-in #41834

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ Standard library changes
#### Random

#### REPL
* `RadioMenu` now supports optional `keybindings` to directly select options ([#41576]).

#### SparseArrays

Expand Down
48 changes: 45 additions & 3 deletions stdlib/REPL/src/TerminalMenus/AbstractMenu.jl
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,13 @@ subtypes.
- `keypress(m::AbstractMenu, i::UInt32)`
- `numoptions(m::AbstractMenu)`
- `selected(m::AbstractMenu)`
- `accepts_vim_bindings(m::AbstractMenu)`
- `accepts_jk(m::AbstractMenu)`
- `accepts_space(m::AbstractMenu)`

!!! compat "Julia 1.7"
The functions `accepts_vim_bindings`, `accepts_jk`, `accepts_space` require
Julia 1.7 or later.
"""
abstract type AbstractMenu end

Expand Down Expand Up @@ -203,9 +209,9 @@ function request(term::REPL.Terminals.TTYTerminal, m::AbstractMenu; cursor::Unio
lastoption = numoptions(m)
c = readkey(term.in_stream)

if c == Int(ARROW_UP) || c == Int('k')
if c == Int(ARROW_UP) || (accepts_jk(m) && c == Int('k'))
cursor[] = move_up!(m, cursor[], lastoption)
elseif c == Int(ARROW_DOWN) || c == Int('j')
elseif c == Int(ARROW_DOWN) || (accepts_jk(m) && c == Int('j'))
cursor[] = move_down!(m, cursor[], lastoption)
elseif c == Int(PAGE_UP)
cursor[] = page_up!(m, cursor[], lastoption)
Expand All @@ -217,7 +223,7 @@ function request(term::REPL.Terminals.TTYTerminal, m::AbstractMenu; cursor::Unio
elseif c == Int(END_KEY)
cursor[] = lastoption
m.pageoffset = lastoption - m.pagesize
elseif c == 13 || c == Int(' ') # <enter> or <space>
elseif c == 13 || (accepts_space(m) && c == Int(' ')) # <enter> or <space>
# will break if pick returns true
pick(m, cursor[]) && break
elseif c == UInt32('q')
Expand Down Expand Up @@ -259,6 +265,42 @@ function request(term::REPL.Terminals.TTYTerminal, msg::AbstractString, m::Abstr
request(term, m; kwargs...)
end

"""
accepts_vim_bindings(m::AbstractMenu)

Returns whether `m` accepts `j` and `k` to move the cursor and Space to pick
the selected option.
Defaults to `false`.
See also: [`accepts_jk`](@ref), [`accepts_space`](@ref).

!!! compat "Julia 1.7"
This function requires Julia 1.7 or later.
"""
accepts_vim_bindings(::AbstractMenu) = false

"""
accepts_jk(m::AbstractMenu)

Returns whether `m` accepts `j`/`k` to move the cursor down/up.
Defaults to `false`.
See also: [`accepts_vim_bindings`](@ref).

!!! compat "Julia 1.7"
This function requires Julia 1.7 or later.
"""
accepts_jk(m::AbstractMenu) = accepts_vim_bindings(m)

"""
accepts_space(m::AbstractMenu)

Returns whether `m` accepts Space to pick the currently selected option.
Defaults to `false`.
See also: [`accepts_vim_bindings`](@ref).

!!! compat "Julia 1.7"
This function requires Julia 1.7 or later.
"""
accepts_space(m::AbstractMenu) = accepts_vim_bindings(m)

function move_up!(m::AbstractMenu, cursor::Int, lastoption::Int=numoptions(m))
if cursor > 1
Expand Down
1 change: 1 addition & 0 deletions stdlib/REPL/src/TerminalMenus/MultiSelectMenu.jl
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ function keypress(menu::MultiSelectMenu, key::UInt32)
false # don't break
end

accepts_vim_bindings(::MultiSelectMenu) = true

## Legacy interface
function TerminalMenus.writeLine(buf::IOBuffer, menu::MultiSelectMenu{<:Dict}, idx::Int, cursor::Bool)
Expand Down
2 changes: 2 additions & 0 deletions stdlib/REPL/src/TerminalMenus/Pager.jl
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ cancel(::Pager) = nothing

pick(::Pager, ::Int) = true

accepts_vim_bindings(::Pager) = true

function writeline(buf::IOBuffer, pager::Pager{Config}, idx::Int, iscursor::Bool)
print(buf, pager.lines[idx])
end
Expand Down
12 changes: 11 additions & 1 deletion stdlib/REPL/src/TerminalMenus/RadioMenu.jl
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@ end

"""

RadioMenu(options::Array{String,1}; pagesize::Int=10, kwargs...)
RadioMenu(options::Array{String,1}; pagesize::Int=10,
keybindings::Vector{Char}=Char[],
kwargs...)

Create a RadioMenu object. Use `request(menu::RadioMenu)` to get user input.
`request()` returns an `Int` which is the index of the option selected by the
Expand All @@ -41,8 +43,12 @@ user.

- `options::Array{String, 1}`: Options to be displayed
- `pagesize::Int=10`: The number of options to be displayed at one time, the menu will scroll if length(options) > pagesize
- `keybindings::Vector{Char}=Char[]`: Shortcuts to pick corresponding entry from `options`

Any additional keyword arguments will be passed to [`TerminalMenus.Config`](@ref).

!!! compat "Julia 1.8"
The `keybindings` argument requires Julia 1.8 or later.
"""
function RadioMenu(options::Array{String,1}; pagesize::Int=10, warn::Bool=true, keybindings::Vector{Char}=Char[], kwargs...)
length(options) < 1 && error("RadioMenu must have at least one option")
Expand Down Expand Up @@ -93,6 +99,10 @@ function keypress(m::RadioMenu, i::UInt32)
return true
end

accepts_jk(m::RadioMenu) = !('j' in m.keybindings || 'k' in m.keybindings)
accepts_space(m::RadioMenu) = !(' ' in m.keybindings)
accepts_vim_bindings(m::RadioMenu) = accepts_jk(m) && accepts_space(m)

# Legacy interface
function writeLine(buf::IOBuffer, menu::RadioMenu{<:Dict}, idx::Int, cursor::Bool)
# print a ">" on the selected entry
Expand Down
2 changes: 2 additions & 0 deletions stdlib/REPL/test/TerminalMenus/multiselect_with_skip_menu.jl
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ function TerminalMenus.keypress(menu::MultiSelectWithSkipMenu, key::UInt32)
false # don't break
end

TerminalMenus.accepts_vim_bindings(::MultiSelectWithSkipMenu) = true

function move_cursor!(menu, direction, selected)
c = menu.cursor[]
while true
Expand Down
8 changes: 8 additions & 0 deletions stdlib/REPL/test/TerminalMenus/radio_menu.jl
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,11 @@ radio_menu = RadioMenu(["single option"], charset=:ascii)
@test simulate_input(1, radio_menu, :up, :up, :down, :up, :enter)
radio_menu = RadioMenu(string.(1:3), pagesize=1, charset=:ascii)
@test simulate_input(3, radio_menu, :down, :down, :down, :down, :enter)
radio_menu = RadioMenu(["apple", "banana", "cherry"]; keybindings=collect('a':'c'), charset=:ascii)
@test simulate_input(2, radio_menu, 'b')
@test TerminalMenus.accepts_vim_bindings(radio_menu)
radio_menu = RadioMenu(string.(1:3); keybindings=['j', '2', '3'], charset=:ascii)
@test TerminalMenus.accepts_space(radio_menu)
@test !TerminalMenus.accepts_jk(radio_menu)
@test !TerminalMenus.accepts_vim_bindings(radio_menu)
@test simulate_input(1, radio_menu, 'k', 'k', :enter)
8 changes: 5 additions & 3 deletions stdlib/REPL/test/TerminalMenus/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,12 @@ function simulate_input(expected, menu::TerminalMenus.AbstractMenu, keys...;
:down => "j",
:enter => " ")
errs = []
got = _simulate_input(keydict, deepcopy(menu), keys...; kwargs...)
if TerminalMenus.accepts_vim_bindings(menu)
got = _simulate_input(vimdict, deepcopy(menu), keys...; kwargs...)
got == expected || push!(errs, :vim => got)
end
got = _simulate_input(keydict, menu, keys...; kwargs...)
got == expected || push!(errs, :arrows => got)
got = _simulate_input(vimdict, menu, keys...; kwargs...)
got == expected || push!(errs, :vim => got)
isempty(errs) || return errs
end

Expand Down