Skip to content

Commit 76a2e36

Browse files
authored
Allow single option with REPL.TerminalMenus (#36369)
1 parent f6d34c3 commit 76a2e36

File tree

9 files changed

+48
-33
lines changed

9 files changed

+48
-33
lines changed

stdlib/REPL/docs/src/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -610,6 +610,7 @@ Aside from the overall `charset` option, for `RadioMenu` the configurable option
610610
- `cursor::Char='>'|'→'`: character to use for cursor
611611
- `up_arrow::Char='^'|'↑'`: character to use for up arrow
612612
- `down_arrow::Char='v'|'↓'`: character to use for down arrow
613+
- `updown_arrow::Char='I'|'↕'`: character to use for up/down arrow in one-line page
613614
- `scroll_wrap::Bool=false`: optionally wrap-around at the beginning/end of a menu
614615
- `ctrl_c_interrupt::Bool=true`: If `false`, return empty on ^C, if `true` throw InterruptException() on ^C
615616

stdlib/REPL/src/TerminalMenus/AbstractMenu.jl

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,8 @@ end
256256
function move_down!(m::AbstractMenu, cursor::Int, lastoption::Int=numoptions(m))
257257
if cursor < lastoption
258258
cursor += 1 # move selection down
259-
if m.pagesize + m.pageoffset <= cursor < lastoption
259+
pagepos = m.pagesize + m.pageoffset
260+
if pagepos <= cursor && pagepos < lastoption
260261
m.pageoffset += 1 # scroll page down
261262
end
262263
elseif scroll_wrap(m)
@@ -315,17 +316,23 @@ function printmenu(out, m::AbstractMenu, cursoridx::Int; oldstate=nothing, init:
315316
# clearline
316317
print(buf, "\x1b[2K")
317318

318-
if i == firstline && m.pageoffset > 0
319-
print_arrow(buf, m, up_arrow(m))
320-
elseif i == lastline && i != lastoption
321-
print_arrow(buf, m, down_arrow(m))
319+
upscrollable = i == firstline && m.pageoffset > 0
320+
downscrollable = i == lastline && i != lastoption
321+
322+
if upscrollable && downscrollable
323+
print(buf, updown_arrow(m))
324+
elseif upscrollable
325+
print(buf, up_arrow(m))
326+
elseif downscrollable
327+
print(buf, down_arrow(m))
322328
else
323-
printcursor(buf, m, i == cursoridx)
329+
print(buf, ' ')
324330
end
325331

332+
printcursor(buf, m, i == cursoridx)
326333
writeline(buf, m, i, i == cursoridx)
327334

328-
i != lastline && print(buf, "\r\n")
335+
(firstline == lastline || i != lastline) && print(buf, "\r\n")
329336
end
330337

331338
newstate = lastline-firstline # final line doesn't have `\n`
@@ -362,10 +369,12 @@ down_arrow(c::AbstractConfig) = down_arrow(c.config)
362369
down_arrow(c::Config) = c.down_arrow
363370
down_arrow(::AbstractMenu) = CONFIG[:down_arrow]
364371

365-
print_arrow(buf, ::ConfiguredMenu, c::Char) = print(buf, c, " ")
366-
print_arrow(buf, ::AbstractMenu, c::Char) = print(buf, c)
372+
updown_arrow(m::ConfiguredMenu) = updown_arrow(m.config)
373+
updown_arrow(c::AbstractConfig) = updown_arrow(c.config)
374+
updown_arrow(c::Config) = c.updown_arrow
375+
updown_arrow(::AbstractMenu) = CONFIG[:updown_arrow]
367376

368-
printcursor(buf, m::ConfiguredMenu, iscursor::Bool) = print(buf, ' ', iscursor ? cursor(m.config) : ' ', ' ')
377+
printcursor(buf, m::ConfiguredMenu, iscursor::Bool) = print(buf, iscursor ? cursor(m.config) : ' ', ' ')
369378
cursor(c::AbstractConfig) = cursor(c.config)
370379
cursor(c::Config) = c.cursor
371-
printcursor(buf, ::AbstractMenu, ::Bool) = print(buf, ' ') # `writeLine` is expected to do the printing (get from CONFIG[:cursor])
380+
printcursor(buf, ::AbstractMenu, ::Bool) = nothing # `writeLine` is expected to do the printing (get from CONFIG[:cursor])

stdlib/REPL/src/TerminalMenus/MultiSelectMenu.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,14 +53,14 @@ were selected by the user.
5353
Any additional keyword arguments will be passed to [`TerminalMenus.MultiSelectConfig`](@ref).
5454
"""
5555
function MultiSelectMenu(options::Array{String,1}; pagesize::Int=10, selected=Int[], warn::Bool=true, kwargs...)
56-
length(options) < 2 && error("MultiSelectMenu must have at least two options")
56+
length(options) < 1 && error("MultiSelectMenu must have at least one option")
5757

5858
# if pagesize is -1, use automatic paging
5959
pagesize = pagesize == -1 ? length(options) : pagesize
6060
# pagesize shouldn't be bigger than options
6161
pagesize = min(length(options), pagesize)
6262
# after other checks, pagesize must be greater than 2
63-
pagesize < 2 && error("pagesize must be >= 2")
63+
pagesize < 1 && error("pagesize must be >= 1")
6464

6565
pageoffset = 0
6666
_selected = Set{Int}()

stdlib/REPL/src/TerminalMenus/RadioMenu.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,14 +44,14 @@ user.
4444
Any additional keyword arguments will be passed to [`TerminalMenus.Config`](@ref).
4545
"""
4646
function RadioMenu(options::Array{String,1}; pagesize::Int=10, warn::Bool=true, kwargs...)
47-
length(options) < 2 && error("RadioMenu must have at least two options")
47+
length(options) < 1 && error("RadioMenu must have at least one option")
4848

4949
# if pagesize is -1, use automatic paging
5050
pagesize = pagesize == -1 ? length(options) : pagesize
5151
# pagesize shouldn't be bigger than options
5252
pagesize = min(length(options), pagesize)
53-
# after other checks, pagesize must be greater than 2
54-
pagesize < 2 && error("pagesize must be >= 2")
53+
# after other checks, pagesize must be greater than 1
54+
pagesize < 1 && error("pagesize must be >= 1")
5555

5656
pageoffset = 0
5757
selected = -1 # none

stdlib/REPL/src/TerminalMenus/config.jl

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ struct Config <: AbstractConfig
66
cursor::Char
77
up_arrow::Char
88
down_arrow::Char
9+
updown_arrow::Char
910
scroll_wrap::Bool
1011
ctrl_c_interrupt::Bool
1112
end
@@ -46,6 +47,7 @@ function Config(;
4647
cursor::Char = '\0',
4748
up_arrow::Char = '\0',
4849
down_arrow::Char = '\0',
50+
updown_arrow::Char = '\0',
4951
scroll_wrap::Bool = false,
5052
ctrl_c_interrupt::Bool = true)
5153
charset === :ascii || charset === :unicode ||
@@ -59,7 +61,10 @@ function Config(;
5961
if down_arrow == '\0'
6062
down_arrow = charset === :ascii ? 'v' : ''
6163
end
62-
return Config(cursor, up_arrow, down_arrow, scroll_wrap, ctrl_c_interrupt)
64+
if updown_arrow == '\0'
65+
updown_arrow = charset === :ascii ? 'I' : ''
66+
end
67+
return Config(cursor, up_arrow, down_arrow, updown_arrow, scroll_wrap, ctrl_c_interrupt)
6368
end
6469

6570
"""
@@ -133,6 +138,7 @@ function config(;charset::Symbol = :na,
133138
cursor::Char = '\0',
134139
up_arrow::Char = '\0',
135140
down_arrow::Char = '\0',
141+
updown_arrow::Char = '\0',
136142
checked::String = "",
137143
unchecked::String = "",
138144
supress_output::Union{Nothing, Bool}=nothing, # typo was documented, unfortunately
@@ -142,12 +148,14 @@ function config(;charset::Symbol = :na,
142148
cursor = '>'
143149
up_arrow = '^'
144150
down_arrow = 'v'
151+
updown_arrow = 'I'
145152
checked = "[X]"
146153
unchecked = "[ ]"
147154
elseif charset === :unicode
148155
cursor = ''
149156
up_arrow = ''
150157
down_arrow = ''
158+
updown_arrow = ''
151159
checked = ""
152160
unchecked = ""
153161
elseif charset === :na
@@ -162,6 +170,7 @@ function config(;charset::Symbol = :na,
162170
cursor != '\0' && (CONFIG[:cursor] = cursor)
163171
up_arrow != '\0' && (CONFIG[:up_arrow] = up_arrow)
164172
down_arrow != '\0' && (CONFIG[:down_arrow] = down_arrow)
173+
updown_arrow != '\0' && (CONFIG[:updown_arrow] = updown_arrow)
165174
checked != "" && (CONFIG[:checked] = checked)
166175
unchecked != "" && (CONFIG[:unchecked] = unchecked)
167176
supress_output isa Bool && (CONFIG[:supress_output] = supress_output)

stdlib/REPL/test/TerminalMenus/legacytests/old_multiselect_menu.jl

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,6 @@
55
# Check to make sure types are imported properly
66
@test MultiSelectMenu <: TerminalMenus.AbstractMenu
77

8-
# Invalid Menu Params
9-
@test_throws ErrorException MultiSelectMenu(["one"], warn=false)
10-
@test_throws ErrorException MultiSelectMenu(["one", "two", "three"], pagesize=1, warn=false)
11-
128
# Constructor
139
@test MultiSelectMenu(["one", "two", "three"], warn=false).pagesize == 3
1410
@test MultiSelectMenu(string.(1:30), pagesize=-1, warn=false).pagesize == 30
@@ -37,3 +33,5 @@ TerminalMenus.writeLine(buf, multi_menu, 1, true)
3733
# Test SDTIN
3834
multi_menu = MultiSelectMenu(string.(1:10), warn=false)
3935
@test simulate_input(Set([1,2]), multi_menu, :enter, :down, :enter, 'd')
36+
multi_menu = MultiSelectMenu(["single option"], warn=false)
37+
@test simulate_input(Set([1]), multi_menu, :up, :up, :down, :enter, 'd')

stdlib/REPL/test/TerminalMenus/legacytests/old_radio_menu.jl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,6 @@
55
# Check to make sure types are imported properly
66
@test RadioMenu <: TerminalMenus.AbstractMenu
77

8-
# Invalid Menu Params
9-
@test_throws ErrorException RadioMenu(["one"], warn=false)
10-
@test_throws ErrorException RadioMenu(["one", "two", "three"], pagesize=1, warn=false)
11-
128
# Constructor
139
@test RadioMenu(["one", "two", "three"], warn=false).pagesize == 3
1410
@test RadioMenu(string.(1:30), pagesize=-1, warn=false).pagesize == 30
@@ -40,3 +36,7 @@ TerminalMenus.writeLine(buf, radio_menu, 1, true)
4036
# Test using stdin
4137
radio_menu = RadioMenu(string.(1:10), warn=false)
4238
@test simulate_input(3, radio_menu, :down, :down, :enter)
39+
radio_menu = RadioMenu(["single option"], warn=false)
40+
@test simulate_input(1, radio_menu, :up, :up, :down, :up, :enter)
41+
radio_menu = RadioMenu(string.(1:3), pagesize=1, warn=false)
42+
@test simulate_input(3, radio_menu, :down, :down, :down, :down, :enter)

stdlib/REPL/test/TerminalMenus/multiselect_menu.jl

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,6 @@
66
# Check to make sure types are imported properly
77
@test MultiSelectMenu{TerminalMenus.MultiSelectConfig} <: TerminalMenus.ConfiguredMenu # TODO Julia 2.0: delete parameter
88

9-
# Invalid Menu Params
10-
@test_throws ErrorException MultiSelectMenu(["one"], charset=:ascii)
11-
@test_throws ErrorException MultiSelectMenu(["one", "two", "three"], pagesize=1, charset=:ascii)
12-
139
# Constructor
1410
@test MultiSelectMenu(["one", "two", "three"], charset=:ascii).pagesize == 3
1511
@test MultiSelectMenu(string.(1:30), pagesize=-1, charset=:ascii).pagesize == 30
@@ -57,3 +53,5 @@ end
5753
# Test SDTIN
5854
multi_menu = MultiSelectMenu(string.(1:10), charset=:ascii)
5955
@test simulate_input(Set([1,2]), multi_menu, :enter, :down, :enter, 'd')
56+
multi_menu = MultiSelectMenu(["single option"], charset=:ascii)
57+
@test simulate_input(Set([1]), multi_menu, :up, :up, :down, :enter, 'd')

stdlib/REPL/test/TerminalMenus/radio_menu.jl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,6 @@
66
# Check to make sure types are imported properly
77
@test RadioMenu{TerminalMenus.Config} <: TerminalMenus.ConfiguredMenu # TODO Julia 2.0: delete parameter
88

9-
# Invalid Menu Params
10-
@test_throws ErrorException RadioMenu(["one"]; charset=:ascii)
11-
@test_throws ErrorException RadioMenu(["one", "two", "three"], pagesize=1, charset=:ascii)
12-
139
# Constructor
1410
@test RadioMenu(["one", "two", "three"]; charset=:ascii).pagesize == 3
1511
@test RadioMenu(string.(1:30), pagesize=-1, charset=:ascii).pagesize == 30
@@ -42,3 +38,7 @@ end
4238
# Test using stdin
4339
radio_menu = RadioMenu(string.(1:10); charset=:ascii)
4440
@test simulate_input(3, radio_menu, :down, :down, :enter)
41+
radio_menu = RadioMenu(["single option"], charset=:ascii)
42+
@test simulate_input(1, radio_menu, :up, :up, :down, :up, :enter)
43+
radio_menu = RadioMenu(string.(1:3), pagesize=1, charset=:ascii)
44+
@test simulate_input(3, radio_menu, :down, :down, :down, :down, :enter)

0 commit comments

Comments
 (0)