From 8ac4e83cc4a5de2dec70c87cf86bab3e935632f9 Mon Sep 17 00:00:00 2001 From: Eleni Lixourioti Date: Fri, 14 May 2021 11:23:33 +0200 Subject: [PATCH 01/33] Add test that demonstrates default list select behaviour --- spec/unit/select_spec.rb | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/spec/unit/select_spec.rb b/spec/unit/select_spec.rb index 475909f..0516c5f 100644 --- a/spec/unit/select_spec.rb +++ b/spec/unit/select_spec.rb @@ -886,6 +886,23 @@ def exit_message(prompt, choice) expect(actual_prompt_output).to eql(expected_prompt_output) end + it "filters and chooses entry with space, with default key config" do + prompt.input << "g" << " " + prompt.input.rewind + + answer = prompt.select("What size?", %i[Small Medium Large Huge], filter: true) + expect(answer).to eql(:Large) + + actual_prompt_output = prompt.output.string + expected_prompt_output = + output_helper("What size?", %w[Small Medium Large Huge], "Small", init: true, + hint: "Press #{up_down} arrow to move, Enter to select and letters to filter") + + output_helper("What size?", %w[Large Huge], "Large", hint: "Filter: \"g\"") + + exit_message("What size?", "Large") + + expect(actual_prompt_output).to eql(expected_prompt_output) + end + it "filters and chooses the first of multiple matching entries" do prompt.input << "g" << "\r" prompt.input.rewind From 993ced286cfef99451d46b93c97af56688bb1812 Mon Sep 17 00:00:00 2001 From: Eleni Lixourioti Date: Fri, 14 May 2021 12:30:37 +0200 Subject: [PATCH 02/33] Fix typo in slider and list show_help --- lib/tty/prompt/list.rb | 2 +- lib/tty/prompt/slider.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/tty/prompt/list.rb b/lib/tty/prompt/list.rb index 3ad585a..250fb67 100644 --- a/lib/tty/prompt/list.rb +++ b/lib/tty/prompt/list.rb @@ -143,7 +143,7 @@ def help(value = (not_set = true)) # # @api public def show_help(value = (not_set = true)) - return @show_ehlp if not_set + return @show_help if not_set @show_help = value end diff --git a/lib/tty/prompt/slider.rb b/lib/tty/prompt/slider.rb index 68453c9..6f40a94 100644 --- a/lib/tty/prompt/slider.rb +++ b/lib/tty/prompt/slider.rb @@ -95,7 +95,7 @@ def help(text = (not_set = true)) # # @api public def show_help(value = (not_set = true)) - return @show_ehlp if not_set + return @show_help if not_set @show_help = value end From 44a179f6f605489e6f57fd6a3fcbb8bf1f067792 Mon Sep 17 00:00:00 2001 From: Eleni Lixourioti Date: Fri, 14 May 2021 18:30:23 +0200 Subject: [PATCH 03/33] Support submit_keys and select_key in (multi_)select --- CHANGELOG.md | 10 ++ README.md | 119 +++++++++++++++++++++++ examples/multi_select_filtered.rb | 8 ++ examples/select_filtered_with_spaces.rb | 11 +++ lib/tty/prompt/list.rb | 62 +++++++++--- lib/tty/prompt/multi_list.rb | 45 +++++++-- spec/unit/multi_select_spec.rb | 65 +++++++++++++ spec/unit/select_spec.rb | 124 ++++++++++++++++-------- 8 files changed, 381 insertions(+), 63 deletions(-) create mode 100644 examples/multi_select_filtered.rb create mode 100644 examples/select_filtered_with_spaces.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index b0e8e38..723643e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ # Change log +## [v0.24.0] - 2021-??-?? + +### Changed +* Change #select and #multi_select to accept a :submit_keys option, instead of always defaulting to space and return +* Change #multi_select to accept a :select_key option, instead of always defaulting to space + +### Fixed +* Fix #select and #multi_select hint to print out the correct submit_keys +* Fix typo in #slider show_help + ## [v0.23.1] - 2021-04-17 ### Changed diff --git a/README.md b/README.md index 8313c85..b4cc6f2 100644 --- a/README.md +++ b/README.md @@ -89,6 +89,7 @@ Or install it yourself as: * [2.6.2.5 :per_page](#2625-per_page) * [2.6.2.6 :disabled](#2626-disabled) * [2.6.2.7 :filter](#2627-filter) + * [2.6.2.8 :submit_keys](#2628-submit_keys) * [2.6.3 multi_select](#263-multi_select) * [2.6.3.1 :cycle](#2631-cycle) * [2.6.3.2 :enum](#2632-enum) @@ -99,6 +100,8 @@ Or install it yourself as: * [2.6.3.7 :filter](#2637-filter) * [2.6.3.8 :min](#2638-min) * [2.6.3.9 :max](#2639-max) + * [2.6.3.10 :submit_keys](#26310-submit_keys) + * [2.6.3.11 :select_key](#262311-select_key) * [2.6.4 enum_select](#264-enum_select) * [2.6.4.1 :per_page](#2641-per_page) * [2.6.4.1 :disabled](#2641-disabled) @@ -916,6 +919,52 @@ Filter characters can be deleted partially or entirely via, respectively, Backsp If the user changes or deletes a filter, the choices previously selected remain selected. +#### 2.6.2.8 `:submit_keys` + +You can configure which key(s) submit your selection (`:space` and `:return` by default): + +```ruby +choices = %w(Scorpion Kano Jax) +prompt.select("Choose your destiny?", choices, submit_keys: [:tab]) +# => +# Choose your destiny? (Press ↑/↓ to move and Tab to select) +# ‣ Scorpion +# Kano +# Jax +``` + +This is particularly useful in conjunction with `:filter`, as you may have choices that include spaces. + +```ruby +choices = ["Jax Sr", "Jax", "Jax Jr"] +prompt.select("Choose your destiny?", choices, submit_keys: [:tab], filter: true) +# => +# Choose your destiny? (Press ↑/↓ to move and Tab to select and letters to filter) +# ‣ Jax Sr +# Jax +# Jax Jr +``` + +After the user types "Jax": + +```ruby +# => +# Choose your destiny? (Filter: "Jax") +# ‣ Jax Sr +# Jax +# Jax Jr +``` + +After the user presses space: +```ruby +# => +# Choose your destiny? (Filter: "Jax ") +# ‣ Jax Sr +# Jax Jr +``` + +Please note that alphanumeric keys are *not* supported. + ### 2.6.3 multi_select For asking questions involving multiple selection list use `multi_select` method by passing the question and possible choices: @@ -1153,6 +1202,76 @@ prompt.multi_select("Select drinks?", choices, max: 3) # ‣ ⬡ bourbon ``` +#### 2.6.3.10 `:submit_keys` + +This works similar to the [same option for `select`](#2628-submit_keys). +You can configure which key(s) submit your selection (`:return` by default): + +```ruby +choices = %w(vodka beer wine whisky bourbon) +prompt.multi_select("Select drinks?", choices, submit_keys: [:tab]) +# => +# Select drinks? vodka, beer, whisky +# ⬢ vodka +# ⬢ beer +# ⬡ wine +# ⬢ whisky +# ‣ ⬡ bourbon +``` + +The user can then press the `tab` key to confirm their selection: +```ruby +# => +# Select drinks? vodka, beer, whisky +``` + +Please note that alphanumeric keys are *not* supported. + +#### 2.6.3.11 `:select_key` + +You can configure which key selects an option (`:space` default). +This is particularly useful in conjunction with the `filter` option, as you may have choices that include spaces. + +```ruby +choices = ["gin", "gin tonic", "gin fizz", "beer"] +prompt.multi_select("Select drinks?", choices, filter: true, select_key: :tab) +# => +# Select drinks? (Press ↑/↓ arrow keys to move, Tab/Ctrl+A|R to select (all|rev), Enter to finish and letters to filter) +# ‣ ⬡ gin +# ⬡ gin tonic +# ⬡ gin fizz +# ⬡ beer +``` + +After the user types "gin": + +```ruby +# => +# Select drinks? (Filter: "gin") +# ‣ ⬡ gin +# ⬡ gin tonic +# ⬡ gin fizz +``` + +and then presses space: + +```ruby +# => +# Select drinks? (Filter: "gin ") +# ‣ ⬡ gin tonic +# ⬡ gin fizz +``` + +The user can then press the `tab` key to select the options: +```ruby +# => +# Select drinks? (Filter: "gin ") +# ‣ ⬢ gin tonic +# ⬡ gin fizz +``` + +Please note that alphanumeric keys are *not* supported. + ### 2.6.4 enum_select In order to ask for standard selection from indexed list you can use `enum_select` and pass question together with possible choices: diff --git a/examples/multi_select_filtered.rb b/examples/multi_select_filtered.rb new file mode 100644 index 0000000..be7f5fa --- /dev/null +++ b/examples/multi_select_filtered.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +require_relative "../lib/tty-prompt" + +prompt = TTY::Prompt.new + +drinks = %w[vodka beer wine whisky bourbon] +prompt.multi_select("Choose your favourite drink?", drinks, filter: true) diff --git a/examples/select_filtered_with_spaces.rb b/examples/select_filtered_with_spaces.rb new file mode 100644 index 0000000..b5d4276 --- /dev/null +++ b/examples/select_filtered_with_spaces.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +require_relative "../lib/tty-prompt" + +prompt = TTY::Prompt.new + +warriors = ["Jax", "Jax Jr", "Kitana", "Raiden ft. Thunder"] + +answer = prompt.select("Choose your destiny?", warriors, filter: true, submit_keys: [:return, :tab]) + +puts answer.inspect diff --git a/lib/tty/prompt/list.rb b/lib/tty/prompt/list.rb index 250fb67..8aaa6f7 100644 --- a/lib/tty/prompt/list.rb +++ b/lib/tty/prompt/list.rb @@ -14,7 +14,7 @@ class Prompt # @api private class List # Allowed keys for filter, along with backspace and canc. - FILTER_KEYS_MATCHER = /\A([[:alnum:]]|[[:punct:]])\Z/.freeze + FILTER_KEYS_MATCHER = /\A([[:alnum:]]|[[:punct:]]|[[:blank:]])\Z/.freeze # Checks type of default parameter to be integer INTEGER_MATCHER = /\A\d+\Z/.freeze @@ -47,6 +47,7 @@ def initialize(prompt, **options) @filterable = options.fetch(:filter) { false } @symbols = @prompt.symbols.merge(options.fetch(:symbols, {})) @quiet = options.fetch(:quiet) { @prompt.quiet } + @submit_keys = options.fetch(:submit_keys) { default_submit_keys } @filter = [] @filter_cache = {} @help = options[:help] @@ -79,6 +80,13 @@ def default(*default_values) @default = default_values end + # Default submit keys, if not explicitly configured + # + # @api private + def default_submit_keys + %i[space return].freeze + end + # Select paginator based on the current navigation key # # @return [Paginator] @@ -163,11 +171,37 @@ def arrows_help arrows.join end + # Convert a key name into a human-readable label + # + # @return [String] + # + # @api private + def key_help_label(key_name) + key_name == :return ? "Enter" : key_name.to_s.capitalize + end + + # Information about keys that submit the selection + # + # @return [String] + # + # @api private + def submit_keys_help + labels = @submit_keys.map(&method(:key_help_label)) + case labels.length + when 1 + labels[0] + when 2 + "#{labels[0]} or #{labels[1]}" + else + "[#{labels.join(',')}]" + end + end + # Default help text # # Note that enumeration and filter are mutually exclusive # - # @a public + # @api public def default_help str = [] str << "(Press " @@ -175,7 +209,7 @@ def default_help str << " or 1-#{choices.size} number" if enumerate? str << " to move" str << (filterable? ? "," : " and") - str << " Enter to select" + str << " #{submit_keys_help} to select" str << " and letters to filter" if filterable? str << ")" str.join @@ -263,12 +297,6 @@ def keynum(event) @active = value end - def keyenter(*) - @done = true unless choices.empty? - end - alias keyreturn keyenter - alias keyspace keyenter - def search_choice_in(searchable) searchable.find { |i| !choices[i - 1].disabled? } end @@ -305,7 +333,6 @@ def keydown(*) @paging_changed = @by_page @by_page = false end - alias keytab keydown # Moves all choices page by page keeping the current selected item # at the same level on each page. @@ -348,10 +375,19 @@ def keyleft(*) end alias keypage_up keyleft - def keypress(event) - return unless filterable? + # Callback fired when a submit key is pressed + # + # @api private + def submit + @done = true unless choices.empty? + end - if event.value =~ FILTER_KEYS_MATCHER + def keypress(event) + if @submit_keys.is_a?(Array) && @submit_keys.include?(event.key.name) + submit + elsif event.key.name == :tab + keydown + elsif filterable? && event.value =~ FILTER_KEYS_MATCHER @filter << event.value @active = 1 end diff --git a/lib/tty/prompt/multi_list.rb b/lib/tty/prompt/multi_list.rb index 0c50043..339b32b 100644 --- a/lib/tty/prompt/multi_list.rb +++ b/lib/tty/prompt/multi_list.rb @@ -19,10 +19,13 @@ class MultiList < List def initialize(prompt, **options) super @selected = SelectedChoices.new + @select_key = options.fetch(:select_key) { :space } @help = options[:help] @echo = options.fetch(:echo, true) @min = options[:min] @max = options[:max] + + check_clashing_keys end # Set a minimum number of choices @@ -39,22 +42,28 @@ def max(value) @max = value end - # Callback fired when enter/return key is pressed + # Default submit keys, if not explicitly configured + # + # @api private + def default_submit_keys + [:return].freeze + end + + # Callback fired when a submit key is pressed # # @api private - def keyenter(*) + def submit valid = true valid = @min <= @selected.size if @min valid = @selected.size <= @max if @max super if valid end - alias keyreturn keyenter - # Callback fired when space key is pressed + # Callback fired when the selection key is pressed # # @api private - def keyspace(*) + def select active_choice = choices[@active - 1] if @selected.include?(active_choice) @selected.delete_at(@active - 1) @@ -65,6 +74,17 @@ def keyspace(*) end end + # Callback fired when any key is pressed + # + # @api private + def keypress(event) + if event.key.name == @select_key + select + else + super(event) + end + end + # Selects all choices when Ctrl+A is pressed # # @api private @@ -89,6 +109,17 @@ def keyctrl_r(*) private + # Checks that there are no key options clashing + # + # @api private + def check_clashing_keys + return unless @submit_keys.include?(@select_key) + + raise ConfigurationError, + ":submit_keys #{@submit_keys} are clashing with " \ + ":select_key (:#{@select_key})" + end + # Setup default options and active selection # # @api private @@ -146,12 +177,12 @@ def default_help str << "(Press " str << "#{arrows_help} arrow" str << " or 1-#{choices.size} number" if enumerate? - str << " to move, Space" + str << " to move, #{key_help_label(@select_key)}" str << "/Ctrl+A|R" if @max.nil? str << " to select" str << " (all|rev)" if @max.nil? str << (filterable? ? "," : " and") - str << " Enter to finish" + str << " #{submit_keys_help} to finish" str << " and letters to filter" if filterable? str << ")" str.join diff --git a/spec/unit/multi_select_spec.rb b/spec/unit/multi_select_spec.rb index bf09b61..ecee7b6 100644 --- a/spec/unit/multi_select_spec.rb +++ b/spec/unit/multi_select_spec.rb @@ -84,6 +84,21 @@ def exit_message(prompt, choices) expect(prompt.output.string).to eq(expected_output) end + it "selects item when custom key pressed" do + choices = %w[vodka beer wine whisky bourbon] + prompt.input << "\t\r" + prompt.input.rewind + expect(prompt.multi_select("Select drinks?", choices, select_key: :tab)).to eq(["vodka"]) + + expected_output = + output_helper("Select drinks?", choices, "vodka", [], init: true, + hint: "Press #{up_down} arrow to move, Tab/Ctrl+A|R to select (all|rev) and Enter to finish") + + output_helper("Select drinks?", choices, "vodka", ["vodka"]) + + exit_message("Select drinks?", %w[vodka]) + + expect(prompt.output.string).to eq(expected_output) + end + it "selects item when space pressed but doesn't echo item if echo: false" do choices = %w[vodka beer wine whisky bourbon] prompt.input << " \r" @@ -251,6 +266,25 @@ def exit_message(prompt, choices) /default index `6` out of range \(1 - 5\)/) end + it "raises error when submit and select keys clash (with default select_key)" do + prompt.input << "\r" + prompt.input.rewind + expect { + prompt.multi_select("Select drinks?", %w[vodka beer wine], submit_keys: [:space]) + }.to raise_error(TTY::Prompt::ConfigurationError, + ":submit_keys [:space] are clashing with :select_key (:space)") + end + + it "raises error when submit and select keys clash (configured)" do + prompt.input << "\r" + prompt.input.rewind + expect { + prompt.multi_select("Select drinks?", %w[vodka beer wine], submit_keys: %i[space tab], select_key: :space) + }.to raise_error(TTY::Prompt::ConfigurationError, + ":submit_keys [:space, :tab] are clashing with :select_key (:space)") + end + + it "sets prompt prefix" do prompt = TTY::Prompt::Test.new(prefix: "[?] ") choices = %w[vodka beer wine whisky bourbon] @@ -709,6 +743,37 @@ def exit_message(prompt, choices) expect(prompt.output.string).to eql(expected_output) end + + it "continues filtering when space is pressed with custom select key" do + choices = ["gin", "gin fizz", "gin tonic"] + prompt.input << "gin" + prompt.input << " " + prompt.input << "f" + prompt.input << "\t" + prompt.input << "\u007F" # Delete one letter + prompt.input << "t" + prompt.input << "\t" + prompt.input << "\r" + prompt.input.rewind + + expect(prompt.multi_select("Select drinks?", choices, filter: true, select_key: :tab)).to eq(["gin fizz", "gin tonic"]) + + expected_output = + output_helper("Select drinks?", choices, "gin", [], init: true, + hint: "Press #{up_down} arrow to move, Tab/Ctrl+A|R to select (all|rev), Enter to finish and letters to filter") + + output_helper("Select drinks?", choices, "gin", [], hint: "Filter: \"g\"") + + output_helper("Select drinks?", choices, "gin", [], hint: "Filter: \"gi\"") + + output_helper("Select drinks?", choices, "gin", [], hint: "Filter: \"gin\"") + + output_helper("Select drinks?", ["gin fizz", "gin tonic"], "gin fizz", [], hint: "Filter: \"gin \"") + + output_helper("Select drinks?", ["gin fizz"], "gin fizz", [], hint: "Filter: \"gin f\"") + + output_helper("Select drinks?", ["gin fizz"], "gin fizz", ["gin fizz"], hint: "Filter: \"gin f\"") + + output_helper("Select drinks?", ["gin fizz", "gin tonic"], "gin fizz", ["gin fizz"], hint: "Filter: \"gin \"") + + output_helper("Select drinks?", ["gin tonic"], "gin tonic", ["gin fizz"], hint: "Filter: \"gin t\"") + + output_helper("Select drinks?", ["gin tonic"], "gin tonic", ["gin fizz", "gin tonic"], hint: "Filter: \"gin t\"") + + exit_message("Select drinks?", ["gin fizz", "gin tonic"]) + + expect(prompt.output.string).to eq(expected_output) + end end context "with :disabled" do diff --git a/spec/unit/select_spec.rb b/spec/unit/select_spec.rb index 0516c5f..cdd2ada 100644 --- a/spec/unit/select_spec.rb +++ b/spec/unit/select_spec.rb @@ -53,7 +53,7 @@ def exit_message(prompt, choice) expected_output = output_helper("What size?", choices, :Large, init: true, - hint: "Press #{up_down} arrow to move and Enter to select") + + hint: "Press #{up_down} arrow to move and Space or Enter to select") + exit_message("What size?", "Large") expect(prompt.output.string).to eq(expected_output) @@ -68,7 +68,7 @@ def exit_message(prompt, choice) end expect { prompt.select("What size?", choices) }.not_to output.to_stderr expect(prompt.output.string).to eq([ - "\e[?25lWhat size? \e[90m(Press #{up_down} arrow to move and Enter to select)\e[0m\n", + "\e[?25lWhat size? \e[90m(Press #{up_down} arrow to move and Space or Enter to select)\e[0m\n", "\e[32m#{symbols[:marker]} Large\e[0m\n", " Medium\n", " Small", @@ -90,7 +90,7 @@ def exit_message(prompt, choice) prompt.input.rewind expect(prompt.select("What size?", choices, default: 1)).to eq(1) expect(prompt.output.string).to eq([ - "\e[?25lWhat size? \e[90m(Press #{up_down} arrow to move and Enter to select)\e[0m\n", + "\e[?25lWhat size? \e[90m(Press #{up_down} arrow to move and Space or Enter to select)\e[0m\n", "\e[32m#{symbols[:marker]} large\e[0m\n", " medium\n", " small", @@ -107,7 +107,7 @@ def exit_message(prompt, choice) expect(prompt.select("What size?", choices, default: 1)).to eq(nil) expect(prompt.output.string).to eq([ output_helper("What size?", choices.keys, :none, init: true, - hint: "Press #{up_down} arrow to move and Enter to select"), + hint: "Press #{up_down} arrow to move and Space or Enter to select"), exit_message("What size?", "none") ].join) end @@ -124,7 +124,7 @@ def exit_message(prompt, choice) end expect(value).to eq("Large") expect(prompt.output.string).to eq([ - "\e[?25lWhat size? \e[90m(Press #{up_down} arrow to move and Enter to select)\e[0m\n", + "\e[?25lWhat size? \e[90m(Press #{up_down} arrow to move and Space or Enter to select)\e[0m\n", "\e[32m> Large\e[0m\n", " Medium\n", " Small", @@ -145,7 +145,7 @@ def exit_message(prompt, choice) end expect(value).to eq(1) expect(prompt.output.string).to eq([ - "\e[?25lWhat size? \e[90m(Press #{up_down} arrow to move and Enter to select)\e[0m\n", + "\e[?25lWhat size? \e[90m(Press #{up_down} arrow to move and Space or Enter to select)\e[0m\n", "\e[32m> large\e[0m\n", " medium\n", " small", @@ -164,7 +164,7 @@ def exit_message(prompt, choice) end expect(value).to eq("Large") expect(prompt.output.string).to eq([ - "\e[?25lWhat size? \e[90m(Press #{up_down} arrow to move and Enter to select)\e[0m\n", + "\e[?25lWhat size? \e[90m(Press #{up_down} arrow to move and Space or Enter to select)\e[0m\n", "\e[32m#{symbols[:marker]} Large\e[0m\n", " Medium\n", " Small", @@ -187,7 +187,7 @@ def exit_message(prompt, choice) end expect(value).to eq(2) expect(prompt.output.string).to eq([ - "\e[?25lWhat size? \e[90m(Press #{up_down} arrow or 1-3 number to move and Enter to select)\e[0m\n", + "\e[?25lWhat size? \e[90m(Press #{up_down} arrow or 1-3 number to move and Space or Enter to select)\e[0m\n", " 1. large\n", "\e[32m#{symbols[:marker]} 2. medium\e[0m\n", " 3. small", @@ -208,7 +208,7 @@ def exit_message(prompt, choice) expect(value).to eq("Good choice!") expect(prompt.output.string).to eq([ - "\e[?25lWhat size? \e[90m(Press #{up_down} arrow or 1-3 number to move and Enter to select)\e[0m\n", + "\e[?25lWhat size? \e[90m(Press #{up_down} arrow or 1-3 number to move and Space or Enter to select)\e[0m\n", " 1) large\n", "\e[32m#{symbols[:marker]} 2) medium\e[0m\n", " 3) small", @@ -224,7 +224,7 @@ def exit_message(prompt, choice) prompt.input.rewind expect(prompt.select("What size?", choices, default: 2, enum: ".")).to eq("Medium") expect(prompt.output.string).to eq([ - "\e[?25lWhat size? \e[90m(Press #{up_down} arrow or 1-3 number to move and Enter to select)\e[0m\n", + "\e[?25lWhat size? \e[90m(Press #{up_down} arrow or 1-3 number to move and Space or Enter to select)\e[0m\n", " 1. Large\n", "\e[32m#{symbols[:marker]} 2. Medium\e[0m\n", " 3. Small", @@ -245,7 +245,7 @@ def exit_message(prompt, choice) expect(value).to eq("Large") expect(prompt.output.string).to eq([ - "\e[?25lWhat size? \e[31m(Press #{up_down} arrow to move and Enter to select)\e[0m\n", + "\e[?25lWhat size? \e[31m(Press #{up_down} arrow to move and Space or Enter to select)\e[0m\n", "\e[34m> Large\e[0m\n", " Medium\n", " Small", @@ -301,7 +301,7 @@ def exit_message(prompt, choice) expected_output = output_helper("What size?", choices.keys, :large, init: true, - hint: "Press #{up_down} arrow to move and Enter to select") + + hint: "Press #{up_down} arrow to move and Space or Enter to select") + "\e[?25h" expect(prompt.output.string).to eq(expected_output) @@ -320,7 +320,7 @@ def exit_message(prompt, choice) expected_output = output_helper("What size?", %w[Large Medium Small], "Large", init: true, - hint: "Press #{up_down} arrow to move and Enter to select") + + hint: "Press #{up_down} arrow to move and Space or Enter to select") + "\e[?25h" expect(prompt.output.string).to eq(expected_output) @@ -333,7 +333,7 @@ def exit_message(prompt, choice) prompt.input.rewind expect(prompt.select("What size?", choices)).to eq("Large") expect(prompt.output.string).to eq([ - "\e[?25l[?] What size? \e[90m(Press #{up_down} arrow to move and Enter to select)\e[0m\n", + "\e[?25l[?] What size? \e[90m(Press #{up_down} arrow to move and Space or Enter to select)\e[0m\n", "\e[32m#{symbols[:marker]} Large\e[0m\n", " Medium\n", " Small", @@ -353,11 +353,11 @@ def exit_message(prompt, choice) expected_output = output_helper("What size?", choices.keys, :large, init: true, - hint: "Press #{up_down} arrow to move and Enter to select") + + hint: "Press #{up_down} arrow to move and Space or Enter to select") + output_helper("What size?", choices.keys, :medium, - hint: "Press #{up_down} arrow to move and Enter to select") + + hint: "Press #{up_down} arrow to move and Space or Enter to select") + output_helper("What size?", choices.keys, :small, - hint: "Press #{up_down} arrow to move and Enter to select") + + hint: "Press #{up_down} arrow to move and Space or Enter to select") + exit_message("What size?", "small") expect(prompt.output.string).to eq(expected_output) @@ -393,7 +393,7 @@ def exit_message(prompt, choice) expect(answer).to eq("D") expected_output = [ - "\e[?25lWhat letter? \e[90m(Press #{up_down}/#{left_right} arrow to move and Enter to select)\e[0m\n", + "\e[?25lWhat letter? \e[90m(Press #{up_down}/#{left_right} arrow to move and Space or Enter to select)\e[0m\n", "\e[32m#{symbols[:marker]} D\e[0m\n", " E\n", " F", @@ -415,7 +415,7 @@ def exit_message(prompt, choice) expect(answer).to eq(4) expected_output = [ - "\e[?25lWhat letter? \e[90m(Press #{up_down}/#{left_right} arrow to move and Enter to select)\e[0m\n", + "\e[?25lWhat letter? \e[90m(Press #{up_down}/#{left_right} arrow to move and Space or Enter to select)\e[0m\n", "\e[32m#{symbols[:marker]} D\e[0m\n", " E\n", " F", @@ -440,7 +440,7 @@ def exit_message(prompt, choice) expect(answer).to eq("D") expected_output = [ - "\e[?25lWhat letter? \e[90m(Press #{up_down}/#{left_right} arrow to move and Enter to select)\e[0m\n", + "\e[?25lWhat letter? \e[90m(Press #{up_down}/#{left_right} arrow to move and Space or Enter to select)\e[0m\n", "\e[32m#{symbols[:marker]} D\e[0m\n", " E\n", " F", @@ -463,7 +463,7 @@ def exit_message(prompt, choice) expected_output = [ output_helper("What number?", choices[0..3], "1", init: true, - hint: "Press #{up_down}/#{left_right} arrow to move and Enter to select"), + hint: "Press #{up_down}/#{left_right} arrow to move and Space or Enter to select"), output_helper("What number?", choices[4..7], "5"), output_helper("What number?", choices[8..11], "9"), output_helper("What number?", choices[8..11], "9"), @@ -485,7 +485,7 @@ def exit_message(prompt, choice) expected_output = [ output_helper("What number?", choices[3..6], "4", init: true, - hint: "Press #{up_down}/#{left_right} arrow to move and Enter to select"), + hint: "Press #{up_down}/#{left_right} arrow to move and Space or Enter to select"), output_helper("What number?", choices[4..7], "8"), output_helper("What number?", choices[8..9], "10"), output_helper("What number?", choices[8..9], "10"), @@ -510,7 +510,7 @@ def exit_message(prompt, choice) expected_output = [ output_helper("What number?", choices[0..3], "2", init: true, - hint: "Press #{up_down}/#{left_right} arrow to move and Enter to select"), + hint: "Press #{up_down}/#{left_right} arrow to move and Space or Enter to select"), output_helper("What number?", choices[4..7], "6"), output_helper("What number?", choices[8..9], "10"), output_helper("What number?", choices[4..7], "6"), @@ -537,7 +537,7 @@ def exit_message(prompt, choice) expected_output = [ output_helper("What number?", choices[0..3], "2", init: true, - hint: "Press #{up_down}/#{left_right} arrow to move and Enter to select"), + hint: "Press #{up_down}/#{left_right} arrow to move and Space or Enter to select"), output_helper("What number?", choices[0..3], "3"), output_helper("What number?", choices[4..7], "7"), output_helper("What number?", choices[4..7], "6"), @@ -576,7 +576,7 @@ def exit_message(prompt, choice) expected_output = [ output_helper("What number?", choices[0..3], "1", init: true, - hint: "Press #{up_down}/#{left_right} arrow to move and Enter to select"), + hint: "Press #{up_down}/#{left_right} arrow to move and Space or Enter to select"), output_helper("What number?", choices[0..3], "3"), output_helper("What number?", choices[2..5], "5"), output_helper("What number?", choices[5..8], "8"), @@ -614,7 +614,7 @@ def exit_message(prompt, choice) expected_output = [ output_helper("What number?", choices[0..3], "2", init: true, - hint: "Press #{up_down}/#{left_right} arrow to move and Enter to select"), + hint: "Press #{up_down}/#{left_right} arrow to move and Space or Enter to select"), output_helper("What number?", choices[4..7], "7"), output_helper("What number?", choices[8..9], "9"), output_helper("What number?", choices[8..9], "9"), @@ -640,7 +640,7 @@ def exit_message(prompt, choice) expect(value).to eq("C") expected_output = [ output_helper("What letter?", choices, "A", init: true, - hint: "Press #{up_down} arrow to move and Enter to select"), + hint: "Press #{up_down} arrow to move and Space or Enter to select"), output_helper("What letter?", choices, "B"), output_helper("What letter?", choices, "C"), output_helper("What letter?", choices, "C"), @@ -660,7 +660,7 @@ def exit_message(prompt, choice) expect(answer).to eq("A") expected_output = [ output_helper("What letter?", choices, "A", init: true, - hint: "Press #{up_down} arrow to move and Enter to select"), + hint: "Press #{up_down} arrow to move and Space or Enter to select"), output_helper("What letter?", choices, "B"), output_helper("What letter?", choices, "C"), output_helper("What letter?", choices, "A"), @@ -685,7 +685,7 @@ def exit_message(prompt, choice) expected_output = output_helper("What letter?", choices, "B", init: true, - hint: "Press #{up_down} arrow to move and Enter to select") + + hint: "Press #{up_down} arrow to move and Space or Enter to select") + output_helper("What letter?", choices, "D") + output_helper("What letter?", choices, "B") + output_helper("What letter?", choices, "D") + @@ -709,7 +709,7 @@ def exit_message(prompt, choice) expected_output = [ output_helper("What number?", choices[0..3], "2", init: true, - hint: "Press #{up_down}/#{left_right} arrow to move and Enter to select"), + hint: "Press #{up_down}/#{left_right} arrow to move and Space or Enter to select"), output_helper("What number?", choices[4..7], "6"), output_helper("What number?", choices[8..9], "10"), output_helper("What number?", choices[0..3], "2"), @@ -747,7 +747,7 @@ def exit_message(prompt, choice) expected_output = [ output_helper("What number?", choices[0..3], "2", init: true, - hint: "Press #{up_down}/#{left_right} arrow to move and Enter to select"), + hint: "Press #{up_down}/#{left_right} arrow to move and Space or Enter to select"), output_helper("What number?", choices[4..7], "7"), output_helper("What number?", choices[8..9], "9"), output_helper("What number?", choices[0..3], "2"), @@ -774,7 +774,7 @@ def exit_message(prompt, choice) expected_output = output_helper("What room?", choices[0..3], "a2", init: true, - hint: "Press #{up_down}/#{left_right} arrow to move, Enter to select and letters to filter") + + hint: "Press #{up_down}/#{left_right} arrow to move, Space or Enter to select and letters to filter") + output_helper("What room?", choices[10..13], "b1", hint: "Filter: \"b\"") + output_helper("What room?", choices[14..17], "b5", hint: "Filter: \"b\"") + output_helper("What room?", choices[18..20], "b9", hint: "Filter: \"b\"") + @@ -838,7 +838,7 @@ def exit_message(prompt, choice) expect(value).to eq("A") expect(prompt.output.string).to eq([ - "\e[?25lWhat letter? \e[90m(Press #{up_down} arrow to move and Enter to select)\e[0m\n", + "\e[?25lWhat letter? \e[90m(Press #{up_down} arrow to move and Space or Enter to select)\e[0m\n", "\e[32m#{symbols[:marker]} A\e[0m\n", " B\n", " C\n", @@ -878,7 +878,7 @@ def exit_message(prompt, choice) expected_prompt_output = output_helper("What size?", %w[Small Medium Large Huge], "Small", init: true, - hint: "Press #{up_down} arrow to move, Enter to select and letters to filter") + + hint: "Press #{up_down} arrow to move, Space or Enter to select and letters to filter") + output_helper("What size?", %w[Medium Huge], "Medium", hint: "Filter: \"U\"") + output_helper("What size?", %w[Huge], "Huge", hint: "Filter: \"Ug\"") + exit_message("What size?", "Huge") @@ -896,13 +896,51 @@ def exit_message(prompt, choice) actual_prompt_output = prompt.output.string expected_prompt_output = output_helper("What size?", %w[Small Medium Large Huge], "Small", init: true, - hint: "Press #{up_down} arrow to move, Enter to select and letters to filter") + + hint: "Press #{up_down} arrow to move, Space or Enter to select and letters to filter") + + output_helper("What size?", %w[Large Huge], "Large", hint: "Filter: \"g\"") + + exit_message("What size?", "Large") + + expect(actual_prompt_output).to eql(expected_prompt_output) + end + + it "filters and chooses entry with tab when configured as submit key" do + prompt.input << "g" << "\t" + prompt.input.rewind + + answer = prompt.select("What size?", %i[Small Medium Large Huge], filter: true, submit_keys: [:tab]) + expect(answer).to eql(:Large) + + actual_prompt_output = prompt.output.string + expected_prompt_output = + output_helper("What size?", %w[Small Medium Large Huge], "Small", init: true, + hint: "Press #{up_down} arrow to move, Tab to select and letters to filter") + output_helper("What size?", %w[Large Huge], "Large", hint: "Filter: \"g\"") + exit_message("What size?", "Large") expect(actual_prompt_output).to eql(expected_prompt_output) end + it "does not choose entry with space, when configured to use only enter" do + prompt.input << "X" << " " << "X" << "\r" + prompt.input.rewind + + choices = ["X Large", "X X Large"] + + answer = prompt.select("What size?", choices, filter: true, submit_keys: [:return]) + expect(answer).to eql("X X Large") + + actual_prompt_output = prompt.output.string + expected_prompt_output = + output_helper("What size?", choices, "X Large", init: true, + hint: "Press #{up_down} arrow to move, Enter to select and letters to filter") + + output_helper("What size?", choices, "X Large", hint: "Filter: \"X\"") + + output_helper("What size?", choices, "X Large", hint: "Filter: \"X \"") + + output_helper("What size?", ["X X Large"], "X X Large", hint: "Filter: \"X X\"") + + exit_message("What size?", "X X Large") + + expect(actual_prompt_output).to eql(expected_prompt_output) + end + it "filters and chooses the first of multiple matching entries" do prompt.input << "g" << "\r" prompt.input.rewind @@ -913,7 +951,7 @@ def exit_message(prompt, choice) actual_prompt_output = prompt.output.string expected_prompt_output = output_helper("What size?", %w[Small Medium Large Huge], "Small", init: true, - hint: "Press #{up_down} arrow to move, Enter to select and letters to filter") + + hint: "Press #{up_down} arrow to move, Space or Enter to select and letters to filter") + output_helper("What size?", %w[Large Huge], "Large", hint: "Filter: \"g\"") + exit_message("What size?", "Large") @@ -930,7 +968,7 @@ def exit_message(prompt, choice) actual_prompt_output = prompt.output.string expected_prompt_output = output_helper("What email?", %w[p*1@mail.com p*2@mail.com p*3@mail.com], "p*1@mail.com", init: true, - hint: "Press #{up_down} arrow to move, Enter to select and letters to filter") + + hint: "Press #{up_down} arrow to move, Space or Enter to select and letters to filter") + output_helper("What email?", %w[p*1@mail.com p*2@mail.com p*3@mail.com], "p*1@mail.com", hint: "Filter: \"p\"") + output_helper("What email?", %w[p*1@mail.com p*2@mail.com p*3@mail.com], "p*1@mail.com", hint: "Filter: \"p*\"") + output_helper("What email?", %w[p*2@mail.com], "p*2@mail.com", hint: "Filter: \"p*2\"") + @@ -952,7 +990,7 @@ def exit_message(prompt, choice) actual_prompt_output = prompt.output.string expected_prompt_output = output_helper("What size?", %w[Tiny Medium Large Huge], "Tiny", init: true, - hint: "Press #{up_down} arrow to move, Enter to select and letters to filter") + + hint: "Press #{up_down} arrow to move, Space or Enter to select and letters to filter") + output_helper("What size?", %w[], "", hint: "Filter: \"z\"") + output_helper("What size?", %w[], "", hint: "Filter: \"z\"") + output_helper("What size?", %w[Large], "Large", hint: "Filter: \"a\"") + @@ -973,7 +1011,7 @@ def exit_message(prompt, choice) expected_prompt_output = output_helper("What size?", %w[Small Medium Large Huge], "Small", init: true, - hint: "Press #{up_down} arrow to move, Enter to select and letters to filter") + + hint: "Press #{up_down} arrow to move, Space or Enter to select and letters to filter") + output_helper("What size?", %w[Huge], "Huge", hint: "Filter: \"H\"") + output_helper("What size?", %w[Huge], "Huge", hint: "Filter: \"Hu\"") + output_helper("What size?", %w[Small], "Small", hint: "Filter: \"S\"") + @@ -996,7 +1034,7 @@ def exit_message(prompt, choice) expected_output = output_helper("What room?", choices[0..3], "a2", init: true, - hint: "Press #{up_down}/#{left_right} arrow to move, Enter to select and letters to filter") + + hint: "Press #{up_down}/#{left_right} arrow to move, Space or Enter to select and letters to filter") + output_helper("What room?", choices[10..13], "b1", hint: "Filter: \"b\"") + output_helper("What room?", choices[14..17], "b5", hint: "Filter: \"b\"") + output_helper("What room?", choices[18..20], "b9", hint: "Filter: \"b\"") + @@ -1022,7 +1060,7 @@ def exit_message(prompt, choice) expected_output = output_helper("What size?", choices, "Small", init: true, - hint: "Press #{up_down} arrow to move and Enter to select") + + hint: "Press #{up_down} arrow to move and Space or Enter to select") + output_helper("What size?", choices, "Medium") + output_helper("What size?", choices, "Huge") + "What size? \e[32mHuge\e[0m\n\e[?25h" @@ -1042,7 +1080,7 @@ def exit_message(prompt, choice) expected_output = output_helper("What letter?", choices, "A", init: true, - hint: "Press #{up_down} arrow to move, Enter to select and letters to filter") + + hint: "Press #{up_down} arrow to move, Space or Enter to select and letters to filter") + output_helper("What letter?", [], "", hint: "Filter: \"c\"") + output_helper("What letter?", [], "", hint: "Filter: \"c\"") + output_helper("What letter?", ["A"], "A", hint: "Filter: \"a\"") + @@ -1066,7 +1104,7 @@ def exit_message(prompt, choice) expected_output = output_helper("What size?", choices, "Small", init: true, enum: ") ", - hint: "Press #{up_down} arrow or 1-3 number to move and Enter to select") + + hint: "Press #{up_down} arrow or 1-3 number to move and Space or Enter to select") + output_helper("What size?", choices, "Small", enum: ") ") + "What size? \e[32mSmall\e[0m\n\e[?25h" From dce79bfca7b4a5ea70ab89e7bbcbbea276b1dd11 Mon Sep 17 00:00:00 2001 From: Eleni Lixourioti Date: Fri, 14 May 2021 18:32:50 +0200 Subject: [PATCH 04/33] Fix hints for #select and #multi_select in README --- README.md | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index b4cc6f2..ab0332c 100644 --- a/README.md +++ b/README.md @@ -161,7 +161,7 @@ Asking question with list of options couldn't be easier using `select` like so: ```ruby prompt.select("Choose your destiny?", %w(Scorpion Kano Jax)) # => -# Choose your destiny? (Use ↑/↓ arrow keys, press Enter to select) +# Choose your destiny? (Press ↑/↓ arrow to move, Space or Enter to select) # ‣ Scorpion # Kano # Jax @@ -639,7 +639,7 @@ By default the choice name is also the value the prompt will return when selecte choices = {small: 1, medium: 2, large: 3} prompt.select("What size?", choices) # => -# What size? (Press ↑/↓ arrow to move and Enter to select) +# What size? (Press ↑/↓ arrow to move and Space or Enter to select) # ‣ small # medium # large @@ -666,7 +666,7 @@ prompt.select("What size?") do |menu| menu.choice name: "large", value: 3 end # => -# What size? (Press ↑/↓ arrow to move and Enter to select) +# What size? (Press ↑/↓ arrow to move and Space or Enter to select) # ‣ small # ✘ medium (out of stock) # large @@ -701,7 +701,7 @@ For asking questions involving list of options use `select` method by passing th ```ruby prompt.select("Choose your destiny?", %w(Scorpion Kano Jax)) # => -# Choose your destiny? (Use ↑/↓ arrow keys, press Enter to select) +# Choose your destiny? (Press ↑/↓ arrow to move and Space or Enter to select) # ‣ Scorpion # Kano # Jax @@ -716,7 +716,7 @@ prompt.select("Choose your destiny?") do |menu| menu.choice "Jax" end # => -# Choose your destiny? (Use ↑/↓ arrow keys, press Enter to select) +# Choose your destiny? (Press ↑/↓ arrow to move and Space or Enter to select) # ‣ Scorpion # Kano # Jax @@ -731,7 +731,7 @@ prompt.select("Choose your destiny?") do |menu| menu.choice "Jax", -> { "Nice choice captain!" } end # => -# Choose your destiny? (Use ↑/↓ arrow keys, press Enter to select) +# Choose your destiny? (Press ↑/↓ arrow to move and Space or Enter to select) # ‣ Scorpion # Kano # Jax @@ -756,7 +756,7 @@ prompt.select("Choose your destiny?") do |menu| menu.choice "Jax", 3 end # => -# Choose your destiny? (Use ↑/↓ arrow keys, press Enter to select) +# Choose your destiny? (Press ↑/↓ arrow to move and Space or Enter to select) # Scorpion # Kano # ‣ Jax @@ -769,7 +769,7 @@ You can navigate the choices using the arrow keys or define your own key mapping ```ruby prompt.select("Choose your destiny?", %w(Scorpion Kano Jax), cycle: true) # => -# Choose your destiny? (Use ↑/↓ arrow keys, press Enter to select) +# Choose your destiny? (Press ↑/↓ arrow to move and Space or Enter to select) # ‣ Scorpion # Kano # Jax @@ -788,7 +788,7 @@ prompt.select("Choose your destiny?") do |menu| menu.choice "Jax", 3 end # => -# Choose your destiny? (Use ↑/↓ arrow or number (0-9) keys, press Enter to select) +# Choose your destiny? (Press ↑/↓ arrow or 1-9 number to move and Space or Enter to select) # 1. Scorpion # 2. Kano # ‣ 3. Jax @@ -816,7 +816,7 @@ You can configure active marker like so: choices = %w(Scorpion Kano Jax) prompt.select("Choose your destiny?", choices, symbols: { marker: ">" }) # => -# Choose your destiny? (Use ↑/↓ and ←/→ arrow keys, press Enter to select) +# Choose your destiny? (Press ↑/↓ to move and Space or Enter to select) # > Scorpion # Kano # Jax @@ -830,7 +830,7 @@ By default the menu is paginated if selection grows beyond `6` items. To change letters = ("A".."Z").to_a prompt.select("Choose your letter?", letters, per_page: 4) # => -# Which letter? (Use ↑/↓ and ←/→ arrow keys, press Enter to select) +# Which letter? (Press ↑/↓/←/→ arrow to move and press Space or Enter to select) # ‣ A # B # C @@ -873,7 +873,7 @@ The disabled choice will be displayed with a cross `✘` character next to it an ```ruby prompt.select("Choose your destiny?", warriors) # => -# Choose your destiny? (Use ↑/↓ arrow keys, press Enter to select) +# Choose your destiny? (Press ↑/↓ arrow to move and Space or Enter to select) # ‣ Scorpion # Kano # ✘ Goro (injury) @@ -890,7 +890,7 @@ To activate dynamic list searching on letter/number key presses use `:filter` op warriors = %w(Scorpion Kano Jax Kitana Raiden) prompt.select("Choose your destiny?", warriors, filter: true) # => -# Choose your destiny? (Use ↑/↓ arrow keys, press Enter to select, and letter keys to filter) +# Choose your destiny? (Press ↑/↓ arrow to move, Space or Enter to select and letters to filter) # ‣ Scorpion # Kano # Jax @@ -974,7 +974,7 @@ choices = %w(vodka beer wine whisky bourbon) prompt.multi_select("Select drinks?", choices) # => # -# Select drinks? (Use ↑/↓ arrow keys, press Space to select and Enter to finish)" +# Select drinks? (Press ↑/↓ arrow keys to move, Space/Ctrl+A|R to select (all|rev) and Enter to finish) # ‣ ⬡ vodka # ⬡ beer # ⬡ wine @@ -1087,7 +1087,7 @@ By default the menu is paginated if selection grows beyond `6` items. To change letters = ("A".."Z").to_a prompt.multi_select("Choose your letter?", letters, per_page: 4) # => -# Which letter? (Use ↑/↓ and ←/→ arrow keys, press Space to select and Enter to finish) +# Which letter? (Press ↑/↓/←/→ arrow keys to move, Space/Ctrl+A|R to select (all|rev) and Enter to finish) # ‣ ⬡ A # ⬡ B # ⬡ C @@ -1114,7 +1114,7 @@ The disabled choice will be displayed with a cross `✘` character next to it an ```ruby prompt.multi_select("Choose your favourite drink?", drinks) # => -# Choose your favourite drink? (Use ↑/↓ arrow keys, press Space to select and Enter to finish) +# Choose your favourite drink? (Press ↑/↓ arrow keys to move, Space/Ctrl+A|R to select (all|rev) and Enter to finish) # ‣ ⬡ bourbon # ✘ sake (out of stock) # ⬡ vodka @@ -1132,7 +1132,7 @@ header use the :echo option: choices = %w(vodka beer wine whisky bourbon) prompt.multi_select("Select drinks?", choices, echo: false) # => -# Select drinks? +# Select drinks? (Press ↑/↓ arrow keys to move, Space/Ctrl+A|R to select (all|rev) and Enter to finish) # ⬡ vodka # ⬢ 2) beer # ⬡ 3) wine @@ -1148,7 +1148,7 @@ To activate dynamic list filtering on letter/number typing, use the :filter opti choices = %w(vodka beer wine whisky bourbon) prompt.multi_select("Select drinks?", choices, filter: true) # => -# Select drinks? (Use ↑/↓ arrow keys, press Space to select and Enter to finish, and letter keys to filter) +# Select drinks? (Press ↑/↓ arrow keys to move, Space/Ctrl+A|R to select (all|rev), Enter to finish and letters to filter) # ‣ ⬡ vodka # ⬡ beer # ⬡ wine From 0a99a1375c139bea4a55cfc8b00f185c783070f9 Mon Sep 17 00:00:00 2001 From: Eleni Lixourioti Date: Sun, 16 May 2021 15:16:33 +0200 Subject: [PATCH 05/33] Rename select to select_choice --- lib/tty/prompt/multi_list.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/tty/prompt/multi_list.rb b/lib/tty/prompt/multi_list.rb index 339b32b..452abca 100644 --- a/lib/tty/prompt/multi_list.rb +++ b/lib/tty/prompt/multi_list.rb @@ -63,7 +63,7 @@ def submit # Callback fired when the selection key is pressed # # @api private - def select + def select_choice active_choice = choices[@active - 1] if @selected.include?(active_choice) @selected.delete_at(@active - 1) @@ -79,7 +79,7 @@ def select # @api private def keypress(event) if event.key.name == @select_key - select + select_choice else super(event) end From 9cb03527563a617dc80a27909c2cdcfa0b8f0f08 Mon Sep 17 00:00:00 2001 From: Eleni Lixourioti Date: Sun, 16 May 2021 15:21:45 +0200 Subject: [PATCH 06/33] Add enter to default submit keys --- lib/tty/prompt/list.rb | 6 +++--- lib/tty/prompt/multi_list.rb | 2 +- spec/unit/select_spec.rb | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 38 insertions(+), 4 deletions(-) diff --git a/lib/tty/prompt/list.rb b/lib/tty/prompt/list.rb index 8aaa6f7..8c2dd6f 100644 --- a/lib/tty/prompt/list.rb +++ b/lib/tty/prompt/list.rb @@ -84,7 +84,7 @@ def default(*default_values) # # @api private def default_submit_keys - %i[space return].freeze + %i[space return enter].freeze end # Select paginator based on the current navigation key @@ -177,7 +177,7 @@ def arrows_help # # @api private def key_help_label(key_name) - key_name == :return ? "Enter" : key_name.to_s.capitalize + %i[return enter].include?(key_name) ? "Enter" : key_name.to_s.capitalize end # Information about keys that submit the selection @@ -186,7 +186,7 @@ def key_help_label(key_name) # # @api private def submit_keys_help - labels = @submit_keys.map(&method(:key_help_label)) + labels = @submit_keys.map(&method(:key_help_label)).uniq case labels.length when 1 labels[0] diff --git a/lib/tty/prompt/multi_list.rb b/lib/tty/prompt/multi_list.rb index 452abca..9237fad 100644 --- a/lib/tty/prompt/multi_list.rb +++ b/lib/tty/prompt/multi_list.rb @@ -46,7 +46,7 @@ def max(value) # # @api private def default_submit_keys - [:return].freeze + %i[return enter].freeze end # Callback fired when a submit key is pressed diff --git a/spec/unit/select_spec.rb b/spec/unit/select_spec.rb index cdd2ada..e726c88 100644 --- a/spec/unit/select_spec.rb +++ b/spec/unit/select_spec.rb @@ -886,6 +886,40 @@ def exit_message(prompt, choice) expect(actual_prompt_output).to eql(expected_prompt_output) end + it "filters and chooses entry with enter, with default key config" do + prompt.input << "g" << "\n" + prompt.input.rewind + + answer = prompt.select("What size?", %i[Small Medium Large Huge], filter: true) + expect(answer).to eql(:Large) + + actual_prompt_output = prompt.output.string + expected_prompt_output = + output_helper("What size?", %w[Small Medium Large Huge], "Small", init: true, + hint: "Press #{up_down} arrow to move, Space or Enter to select and letters to filter") + + output_helper("What size?", %w[Large Huge], "Large", hint: "Filter: \"g\"") + + exit_message("What size?", "Large") + + expect(actual_prompt_output).to eql(expected_prompt_output) + end + + it "filters and chooses entry with return and enter, with default key config" do + prompt.input << "g" << "\r\n" + prompt.input.rewind + + answer = prompt.select("What size?", %i[Small Medium Large Huge], filter: true) + expect(answer).to eql(:Large) + + actual_prompt_output = prompt.output.string + expected_prompt_output = + output_helper("What size?", %w[Small Medium Large Huge], "Small", init: true, + hint: "Press #{up_down} arrow to move, Space or Enter to select and letters to filter") + + output_helper("What size?", %w[Large Huge], "Large", hint: "Filter: \"g\"") + + exit_message("What size?", "Large") + + expect(actual_prompt_output).to eql(expected_prompt_output) + end + it "filters and chooses entry with space, with default key config" do prompt.input << "g" << " " prompt.input.rewind From 5327ce9ea8bde90e01145aac64ae6cbbeb0714cf Mon Sep 17 00:00:00 2001 From: Eleni Lixourioti Date: Sun, 16 May 2021 15:38:14 +0200 Subject: [PATCH 07/33] Replace :tab with :ctrl_s as submit key in examples, docs and tests --- README.md | 18 +++++++++--------- examples/select_filtered_with_spaces.rb | 3 ++- lib/tty/prompt/list.rb | 6 +++++- spec/unit/multi_select_spec.rb | 18 +++++++++--------- spec/unit/select_spec.rb | 8 ++++---- 5 files changed, 29 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index ab0332c..0adae4b 100644 --- a/README.md +++ b/README.md @@ -925,9 +925,9 @@ You can configure which key(s) submit your selection (`:space` and `:return` by ```ruby choices = %w(Scorpion Kano Jax) -prompt.select("Choose your destiny?", choices, submit_keys: [:tab]) +prompt.select("Choose your destiny?", choices, submit_keys: [:ctrl_s]) # => -# Choose your destiny? (Press ↑/↓ to move and Tab to select) +# Choose your destiny? (Press ↑/↓ to move and Ctrl+S to select) # ‣ Scorpion # Kano # Jax @@ -937,9 +937,9 @@ This is particularly useful in conjunction with `:filter`, as you may have choic ```ruby choices = ["Jax Sr", "Jax", "Jax Jr"] -prompt.select("Choose your destiny?", choices, submit_keys: [:tab], filter: true) +prompt.select("Choose your destiny?", choices, submit_keys: [:ctrl_s], filter: true) # => -# Choose your destiny? (Press ↑/↓ to move and Tab to select and letters to filter) +# Choose your destiny? (Press ↑/↓ to move and Ctrl+S to select and letters to filter) # ‣ Jax Sr # Jax # Jax Jr @@ -1209,7 +1209,7 @@ You can configure which key(s) submit your selection (`:return` by default): ```ruby choices = %w(vodka beer wine whisky bourbon) -prompt.multi_select("Select drinks?", choices, submit_keys: [:tab]) +prompt.multi_select("Select drinks?", choices, submit_keys: [:ctrl_s]) # => # Select drinks? vodka, beer, whisky # ⬢ vodka @@ -1219,7 +1219,7 @@ prompt.multi_select("Select drinks?", choices, submit_keys: [:tab]) # ‣ ⬡ bourbon ``` -The user can then press the `tab` key to confirm their selection: +The user can then press the `Ctrl+S` key to confirm their selection: ```ruby # => # Select drinks? vodka, beer, whisky @@ -1234,9 +1234,9 @@ This is particularly useful in conjunction with the `filter` option, as you may ```ruby choices = ["gin", "gin tonic", "gin fizz", "beer"] -prompt.multi_select("Select drinks?", choices, filter: true, select_key: :tab) +prompt.multi_select("Select drinks?", choices, filter: true, select_key: :ctrl_s) # => -# Select drinks? (Press ↑/↓ arrow keys to move, Tab/Ctrl+A|R to select (all|rev), Enter to finish and letters to filter) +# Select drinks? (Press ↑/↓ arrow keys to move, Ctrl+S/Ctrl+A|R to select (all|rev), Enter to finish and letters to filter) # ‣ ⬡ gin # ⬡ gin tonic # ⬡ gin fizz @@ -1262,7 +1262,7 @@ and then presses space: # ⬡ gin fizz ``` -The user can then press the `tab` key to select the options: +The user can then press the `Ctrl+S` key combo to select the options: ```ruby # => # Select drinks? (Filter: "gin ") diff --git a/examples/select_filtered_with_spaces.rb b/examples/select_filtered_with_spaces.rb index b5d4276..2129a31 100644 --- a/examples/select_filtered_with_spaces.rb +++ b/examples/select_filtered_with_spaces.rb @@ -6,6 +6,7 @@ warriors = ["Jax", "Jax Jr", "Kitana", "Raiden ft. Thunder"] -answer = prompt.select("Choose your destiny?", warriors, filter: true, submit_keys: [:return, :tab]) +answer = prompt.select("Choose your destiny?", warriors, + filter: true, submit_keys: %i[return ctrl_s]) puts answer.inspect diff --git a/lib/tty/prompt/list.rb b/lib/tty/prompt/list.rb index 8c2dd6f..534b158 100644 --- a/lib/tty/prompt/list.rb +++ b/lib/tty/prompt/list.rb @@ -177,7 +177,11 @@ def arrows_help # # @api private def key_help_label(key_name) - %i[return enter].include?(key_name) ? "Enter" : key_name.to_s.capitalize + if %i[return enter].include?(key_name) + "Enter" + else + key_name.to_s.split("_").map(&:capitalize).join("+") + end end # Information about keys that submit the selection diff --git a/spec/unit/multi_select_spec.rb b/spec/unit/multi_select_spec.rb index ecee7b6..1e6a4eb 100644 --- a/spec/unit/multi_select_spec.rb +++ b/spec/unit/multi_select_spec.rb @@ -86,13 +86,13 @@ def exit_message(prompt, choices) it "selects item when custom key pressed" do choices = %w[vodka beer wine whisky bourbon] - prompt.input << "\t\r" + prompt.input << "\C-s\r" prompt.input.rewind - expect(prompt.multi_select("Select drinks?", choices, select_key: :tab)).to eq(["vodka"]) + expect(prompt.multi_select("Select drinks?", choices, select_key: :ctrl_s)).to eq(["vodka"]) expected_output = output_helper("Select drinks?", choices, "vodka", [], init: true, - hint: "Press #{up_down} arrow to move, Tab/Ctrl+A|R to select (all|rev) and Enter to finish") + + hint: "Press #{up_down} arrow to move, Ctrl+S/Ctrl+A|R to select (all|rev) and Enter to finish") + output_helper("Select drinks?", choices, "vodka", ["vodka"]) + exit_message("Select drinks?", %w[vodka]) @@ -279,9 +279,9 @@ def exit_message(prompt, choices) prompt.input << "\r" prompt.input.rewind expect { - prompt.multi_select("Select drinks?", %w[vodka beer wine], submit_keys: %i[space tab], select_key: :space) + prompt.multi_select("Select drinks?", %w[vodka beer wine], submit_keys: %i[space ctrl_s], select_key: :space) }.to raise_error(TTY::Prompt::ConfigurationError, - ":submit_keys [:space, :tab] are clashing with :select_key (:space)") + ":submit_keys [:space, :ctrl_s] are clashing with :select_key (:space)") end @@ -749,18 +749,18 @@ def exit_message(prompt, choices) prompt.input << "gin" prompt.input << " " prompt.input << "f" - prompt.input << "\t" + prompt.input << "\C-s" prompt.input << "\u007F" # Delete one letter prompt.input << "t" - prompt.input << "\t" + prompt.input << "\C-s" prompt.input << "\r" prompt.input.rewind - expect(prompt.multi_select("Select drinks?", choices, filter: true, select_key: :tab)).to eq(["gin fizz", "gin tonic"]) + expect(prompt.multi_select("Select drinks?", choices, filter: true, select_key: :ctrl_s)).to eq(["gin fizz", "gin tonic"]) expected_output = output_helper("Select drinks?", choices, "gin", [], init: true, - hint: "Press #{up_down} arrow to move, Tab/Ctrl+A|R to select (all|rev), Enter to finish and letters to filter") + + hint: "Press #{up_down} arrow to move, Ctrl+S/Ctrl+A|R to select (all|rev), Enter to finish and letters to filter") + output_helper("Select drinks?", choices, "gin", [], hint: "Filter: \"g\"") + output_helper("Select drinks?", choices, "gin", [], hint: "Filter: \"gi\"") + output_helper("Select drinks?", choices, "gin", [], hint: "Filter: \"gin\"") + diff --git a/spec/unit/select_spec.rb b/spec/unit/select_spec.rb index e726c88..a68b047 100644 --- a/spec/unit/select_spec.rb +++ b/spec/unit/select_spec.rb @@ -937,17 +937,17 @@ def exit_message(prompt, choice) expect(actual_prompt_output).to eql(expected_prompt_output) end - it "filters and chooses entry with tab when configured as submit key" do - prompt.input << "g" << "\t" + it "filters and chooses entry with Ctrl+S when configured as submit key" do + prompt.input << "g" << "\C-s" prompt.input.rewind - answer = prompt.select("What size?", %i[Small Medium Large Huge], filter: true, submit_keys: [:tab]) + answer = prompt.select("What size?", %i[Small Medium Large Huge], filter: true, submit_keys: [:ctrl_s]) expect(answer).to eql(:Large) actual_prompt_output = prompt.output.string expected_prompt_output = output_helper("What size?", %w[Small Medium Large Huge], "Small", init: true, - hint: "Press #{up_down} arrow to move, Tab to select and letters to filter") + + hint: "Press #{up_down} arrow to move, Ctrl+S to select and letters to filter") + output_helper("What size?", %w[Large Huge], "Large", hint: "Filter: \"g\"") + exit_message("What size?", "Large") From b24819dedf26f0f13f67f0ebdc894883ee837dd3 Mon Sep 17 00:00:00 2001 From: Eleni Lixourioti Date: Sun, 16 May 2021 16:23:55 +0200 Subject: [PATCH 08/33] Allow defining the label of submit keys --- README.md | 14 ++++++++++++- examples/select_submit_key_labels.rb | 12 +++++++++++ lib/tty/prompt/list.rb | 31 ++++++++++++++++++++-------- lib/tty/prompt/multi_list.rb | 2 +- spec/unit/select_spec.rb | 17 +++++++++++++++ 5 files changed, 65 insertions(+), 11 deletions(-) create mode 100644 examples/select_submit_key_labels.rb diff --git a/README.md b/README.md index 0adae4b..ba95188 100644 --- a/README.md +++ b/README.md @@ -939,7 +939,7 @@ This is particularly useful in conjunction with `:filter`, as you may have choic choices = ["Jax Sr", "Jax", "Jax Jr"] prompt.select("Choose your destiny?", choices, submit_keys: [:ctrl_s], filter: true) # => -# Choose your destiny? (Press ↑/↓ to move and Ctrl+S to select and letters to filter) +# Choose your destiny? (Press ↑/↓ to move, Ctrl+S to select and letters to filter) # ‣ Jax Sr # Jax # Jax Jr @@ -963,6 +963,18 @@ After the user presses space: # Jax Jr ``` +You may also pass your own key labels to be displayed in the hint: + +```ruby +choices = ["Jax Sr", "Jax", "Jax Jr"] +prompt.select("Choose your destiny?", choices, submit_keys: [:enter, {escape: "ESC"}]) +# => +# Choose your destiny? (Press ↑/↓ to move and Enter or ESC to select) +# ‣ Jax Sr +# Jax +# Jax Jr +``` + Please note that alphanumeric keys are *not* supported. ### 2.6.3 multi_select diff --git a/examples/select_submit_key_labels.rb b/examples/select_submit_key_labels.rb new file mode 100644 index 0000000..b362e8f --- /dev/null +++ b/examples/select_submit_key_labels.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +require_relative "../lib/tty-prompt" + +prompt = TTY::Prompt.new + +warriors = %w[Scorpion Kano Jax Kitana Raiden] + +answer = prompt.select("Choose your destiny?", warriors, + submit_keys: [:enter, {escape: "ESC"}]) + +puts answer.inspect \ No newline at end of file diff --git a/lib/tty/prompt/list.rb b/lib/tty/prompt/list.rb index 534b158..0866ea0 100644 --- a/lib/tty/prompt/list.rb +++ b/lib/tty/prompt/list.rb @@ -47,7 +47,7 @@ def initialize(prompt, **options) @filterable = options.fetch(:filter) { false } @symbols = @prompt.symbols.merge(options.fetch(:symbols, {})) @quiet = options.fetch(:quiet) { @prompt.quiet } - @submit_keys = options.fetch(:submit_keys) { default_submit_keys } + @submit_keys = keys_with_labels(options.fetch(:submit_keys) { default_submit_keys }) @filter = [] @filter_cache = {} @help = options[:help] @@ -171,13 +171,29 @@ def arrows_help arrows.join end + # Normalize a list of key symbols or symbol-label hashes + # into a single symbol-label lookup hash. + # + # @return [String] + # + # @api private + def keys_with_labels(keys) + keys.reduce({}) { |result, key| + if key.is_a?(Hash) + result.merge(key) + else + result.merge({key => key_help_label(key)}) + end + } + end + # Convert a key name into a human-readable label # # @return [String] # # @api private def key_help_label(key_name) - if %i[return enter].include?(key_name) + if key_name == :return "Enter" else key_name.to_s.split("_").map(&:capitalize).join("+") @@ -190,14 +206,11 @@ def key_help_label(key_name) # # @api private def submit_keys_help - labels = @submit_keys.map(&method(:key_help_label)).uniq - case labels.length - when 1 + labels = @submit_keys.values.uniq + if labels.length == 1 labels[0] - when 2 - "#{labels[0]} or #{labels[1]}" else - "[#{labels.join(',')}]" + "#{labels[0..-2].join(', ')} or #{labels[-1]}" end end @@ -387,7 +400,7 @@ def submit end def keypress(event) - if @submit_keys.is_a?(Array) && @submit_keys.include?(event.key.name) + if @submit_keys.include?(event.key.name) submit elsif event.key.name == :tab keydown diff --git a/lib/tty/prompt/multi_list.rb b/lib/tty/prompt/multi_list.rb index 9237fad..9e162d6 100644 --- a/lib/tty/prompt/multi_list.rb +++ b/lib/tty/prompt/multi_list.rb @@ -116,7 +116,7 @@ def check_clashing_keys return unless @submit_keys.include?(@select_key) raise ConfigurationError, - ":submit_keys #{@submit_keys} are clashing with " \ + ":submit_keys #{@submit_keys.keys} are clashing with " \ ":select_key (:#{@select_key})" end diff --git a/spec/unit/select_spec.rb b/spec/unit/select_spec.rb index a68b047..7382435 100644 --- a/spec/unit/select_spec.rb +++ b/spec/unit/select_spec.rb @@ -954,6 +954,23 @@ def exit_message(prompt, choice) expect(actual_prompt_output).to eql(expected_prompt_output) end + it "filters and chooses entry with Escape and custom label when configured as submit key" do + prompt.input << "g" << "\e" + prompt.input.rewind + + answer = prompt.select("What size?", %i[Small Medium Large Huge], filter: true, submit_keys: [escape: "Esc"]) + expect(answer).to eql(:Large) + + actual_prompt_output = prompt.output.string + expected_prompt_output = + output_helper("What size?", %w[Small Medium Large Huge], "Small", init: true, + hint: "Press #{up_down} arrow to move, Esc to select and letters to filter") + + output_helper("What size?", %w[Large Huge], "Large", hint: "Filter: \"g\"") + + exit_message("What size?", "Large") + + expect(actual_prompt_output).to eql(expected_prompt_output) + end + it "does not choose entry with space, when configured to use only enter" do prompt.input << "X" << " " << "X" << "\r" prompt.input.rewind From 135e62ac188098360dfc1cedb9a1e67614d27f54 Mon Sep 17 00:00:00 2001 From: Eleni Lixourioti Date: Sun, 16 May 2021 16:28:59 +0200 Subject: [PATCH 09/33] Add author handle in CHANGELOG --- CHANGELOG.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 723643e..29c8de2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,12 +3,12 @@ ## [v0.24.0] - 2021-??-?? ### Changed -* Change #select and #multi_select to accept a :submit_keys option, instead of always defaulting to space and return -* Change #multi_select to accept a :select_key option, instead of always defaulting to space +* Change #select and #multi_select to accept a :submit_keys option, instead of always defaulting to space and return by Eleni Lixourioti(@Geekfish) +* Change #multi_select to accept a :select_keys option, instead of always defaulting to space by Eleni Lixourioti(@Geekfish) ### Fixed -* Fix #select and #multi_select hint to print out the correct submit_keys -* Fix typo in #slider show_help +* Fix #select and #multi_select hint to print out the correct submit_keys by Eleni Lixourioti(@Geekfish) +* Fix typo in #slider show_help(@Geekfish) ## [v0.23.1] - 2021-04-17 From 395e6b151f4a5d4a89548b74b2f48d70941b5e73 Mon Sep 17 00:00:00 2001 From: Eleni Lixourioti Date: Sun, 16 May 2021 16:41:03 +0200 Subject: [PATCH 10/33] Make multi-select select keys an array and allow custom labels --- README.md | 19 +++++++++++++++--- examples/multi_select_custom_keys.rb | 10 ++++++++++ lib/tty/prompt/list.rb | 6 +++--- lib/tty/prompt/multi_list.rb | 12 ++++++------ spec/unit/multi_select_spec.rb | 29 +++++++++++++++++++++------- 5 files changed, 57 insertions(+), 19 deletions(-) create mode 100644 examples/multi_select_custom_keys.rb diff --git a/README.md b/README.md index ba95188..27d62f4 100644 --- a/README.md +++ b/README.md @@ -101,7 +101,7 @@ Or install it yourself as: * [2.6.3.8 :min](#2638-min) * [2.6.3.9 :max](#2639-max) * [2.6.3.10 :submit_keys](#26310-submit_keys) - * [2.6.3.11 :select_key](#262311-select_key) + * [2.6.3.11 :select_keys](#262311-select_keys) * [2.6.4 enum_select](#264-enum_select) * [2.6.4.1 :per_page](#2641-per_page) * [2.6.4.1 :disabled](#2641-disabled) @@ -1239,14 +1239,14 @@ The user can then press the `Ctrl+S` key to confirm their selection: Please note that alphanumeric keys are *not* supported. -#### 2.6.3.11 `:select_key` +#### 2.6.3.11 `:select_keys` You can configure which key selects an option (`:space` default). This is particularly useful in conjunction with the `filter` option, as you may have choices that include spaces. ```ruby choices = ["gin", "gin tonic", "gin fizz", "beer"] -prompt.multi_select("Select drinks?", choices, filter: true, select_key: :ctrl_s) +prompt.multi_select("Select drinks?", choices, filter: true, select_keys: [:ctrl_s]) # => # Select drinks? (Press ↑/↓ arrow keys to move, Ctrl+S/Ctrl+A|R to select (all|rev), Enter to finish and letters to filter) # ‣ ⬡ gin @@ -1282,6 +1282,19 @@ The user can then press the `Ctrl+S` key combo to select the options: # ⬡ gin fizz ``` +Similar to the `:submit_keys` option, you may also pass your own key labels to be displayed in the hint: + +```ruby +choices = ["gin", "gin tonic", "gin fizz", "beer"] +prompt.multi_select("Select drinks?", choices, filter: true, select_keys: [{ctr_s: "Ctrl-S"}, {space: "Spacebar"}]) +# => +# Select drinks? (Press ↑/↓ arrow keys to move, Ctrl-S or Spacebar/Ctrl+A|R to select (all|rev), Enter to finish and letters to filter) +# ‣ ⬡ gin +# ⬡ gin tonic +# ⬡ gin fizz +# ⬡ beer +``` + Please note that alphanumeric keys are *not* supported. ### 2.6.4 enum_select diff --git a/examples/multi_select_custom_keys.rb b/examples/multi_select_custom_keys.rb new file mode 100644 index 0000000..ad12b9c --- /dev/null +++ b/examples/multi_select_custom_keys.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +require_relative "../lib/tty-prompt" + +prompt = TTY::Prompt.new + +drinks = %w[vodka beer wine whisky bourbon] +prompt.multi_select("Choose your favourite drink?", drinks, + submit_keys: [:return, {escape: "Esc"}], + select_keys: [{space: "Spacebar"}, :ctrl_s]) diff --git a/lib/tty/prompt/list.rb b/lib/tty/prompt/list.rb index 0866ea0..f2c82dc 100644 --- a/lib/tty/prompt/list.rb +++ b/lib/tty/prompt/list.rb @@ -205,8 +205,8 @@ def key_help_label(key_name) # @return [String] # # @api private - def submit_keys_help - labels = @submit_keys.values.uniq + def keys_help(keys) + labels = keys.values.uniq if labels.length == 1 labels[0] else @@ -226,7 +226,7 @@ def default_help str << " or 1-#{choices.size} number" if enumerate? str << " to move" str << (filterable? ? "," : " and") - str << " #{submit_keys_help} to select" + str << " #{keys_help(@submit_keys)} to select" str << " and letters to filter" if filterable? str << ")" str.join diff --git a/lib/tty/prompt/multi_list.rb b/lib/tty/prompt/multi_list.rb index 9e162d6..8ffd805 100644 --- a/lib/tty/prompt/multi_list.rb +++ b/lib/tty/prompt/multi_list.rb @@ -19,7 +19,7 @@ class MultiList < List def initialize(prompt, **options) super @selected = SelectedChoices.new - @select_key = options.fetch(:select_key) { :space } + @select_keys = keys_with_labels(options.fetch(:select_keys, [:space])) @help = options[:help] @echo = options.fetch(:echo, true) @min = options[:min] @@ -78,7 +78,7 @@ def select_choice # # @api private def keypress(event) - if event.key.name == @select_key + if @select_keys.include?(event.key.name) select_choice else super(event) @@ -113,11 +113,11 @@ def keyctrl_r(*) # # @api private def check_clashing_keys - return unless @submit_keys.include?(@select_key) + return if (@submit_keys.keys & @select_keys.keys).empty? raise ConfigurationError, ":submit_keys #{@submit_keys.keys} are clashing with " \ - ":select_key (:#{@select_key})" + ":select_keys #{@select_keys.keys}" end # Setup default options and active selection @@ -177,12 +177,12 @@ def default_help str << "(Press " str << "#{arrows_help} arrow" str << " or 1-#{choices.size} number" if enumerate? - str << " to move, #{key_help_label(@select_key)}" + str << " to move, #{keys_help(@select_keys)}" str << "/Ctrl+A|R" if @max.nil? str << " to select" str << " (all|rev)" if @max.nil? str << (filterable? ? "," : " and") - str << " #{submit_keys_help} to finish" + str << " #{keys_help(@submit_keys)} to finish" str << " and letters to filter" if filterable? str << ")" str.join diff --git a/spec/unit/multi_select_spec.rb b/spec/unit/multi_select_spec.rb index 1e6a4eb..e369863 100644 --- a/spec/unit/multi_select_spec.rb +++ b/spec/unit/multi_select_spec.rb @@ -84,15 +84,30 @@ def exit_message(prompt, choices) expect(prompt.output.string).to eq(expected_output) end - it "selects item when custom key pressed" do + it "selects item when custom key pressed and shows custom key labels" do choices = %w[vodka beer wine whisky bourbon] prompt.input << "\C-s\r" prompt.input.rewind - expect(prompt.multi_select("Select drinks?", choices, select_key: :ctrl_s)).to eq(["vodka"]) + expect(prompt.multi_select("Select drinks?", choices, select_keys: [:ctrl_s, {escape: "Esc"}])).to eq(["vodka"]) expected_output = output_helper("Select drinks?", choices, "vodka", [], init: true, - hint: "Press #{up_down} arrow to move, Ctrl+S/Ctrl+A|R to select (all|rev) and Enter to finish") + + hint: "Press #{up_down} arrow to move, Ctrl+S or Esc/Ctrl+A|R to select (all|rev) and Enter to finish") + + output_helper("Select drinks?", choices, "vodka", ["vodka"]) + + exit_message("Select drinks?", %w[vodka]) + + expect(prompt.output.string).to eq(expected_output) + end + + it "selects item and submits selection with custom keys" do + choices = %w[vodka beer wine whisky bourbon] + prompt.input << "\C-s\e" + prompt.input.rewind + expect(prompt.multi_select("Select drinks?", choices, select_keys: [:ctrl_s], submit_keys: [{escape: "Esc"}])).to eq(["vodka"]) + + expected_output = + output_helper("Select drinks?", choices, "vodka", [], init: true, + hint: "Press #{up_down} arrow to move, Ctrl+S/Ctrl+A|R to select (all|rev) and Esc to finish") + output_helper("Select drinks?", choices, "vodka", ["vodka"]) + exit_message("Select drinks?", %w[vodka]) @@ -272,16 +287,16 @@ def exit_message(prompt, choices) expect { prompt.multi_select("Select drinks?", %w[vodka beer wine], submit_keys: [:space]) }.to raise_error(TTY::Prompt::ConfigurationError, - ":submit_keys [:space] are clashing with :select_key (:space)") + ":submit_keys [:space] are clashing with :select_keys [:space]") end it "raises error when submit and select keys clash (configured)" do prompt.input << "\r" prompt.input.rewind expect { - prompt.multi_select("Select drinks?", %w[vodka beer wine], submit_keys: %i[space ctrl_s], select_key: :space) + prompt.multi_select("Select drinks?", %w[vodka beer wine], submit_keys: %i[space ctrl_s], select_keys: [:space]) }.to raise_error(TTY::Prompt::ConfigurationError, - ":submit_keys [:space, :ctrl_s] are clashing with :select_key (:space)") + ":submit_keys [:space, :ctrl_s] are clashing with :select_keys [:space]") end @@ -756,7 +771,7 @@ def exit_message(prompt, choices) prompt.input << "\r" prompt.input.rewind - expect(prompt.multi_select("Select drinks?", choices, filter: true, select_key: :ctrl_s)).to eq(["gin fizz", "gin tonic"]) + expect(prompt.multi_select("Select drinks?", choices, filter: true, select_keys: [:ctrl_s])).to eq(["gin fizz", "gin tonic"]) expected_output = output_helper("Select drinks?", choices, "gin", [], init: true, From 3fa9f2cd6482fb8e9fcaa103192b4a958adc7697 Mon Sep 17 00:00:00 2001 From: Eleni Lixourioti Date: Sun, 16 May 2021 18:26:01 +0200 Subject: [PATCH 11/33] Ensure cross-system compat for EOL chars in custom action keys --- lib/tty/prompt/list.rb | 37 +++++++++++++++++++++++++++++++++++- lib/tty/prompt/multi_list.rb | 2 +- spec/unit/select_spec.rb | 34 +++++++++++++++++++++++++++++++++ 3 files changed, 71 insertions(+), 2 deletions(-) diff --git a/lib/tty/prompt/list.rb b/lib/tty/prompt/list.rb index f2c82dc..a784ad9 100644 --- a/lib/tty/prompt/list.rb +++ b/lib/tty/prompt/list.rb @@ -47,7 +47,7 @@ def initialize(prompt, **options) @filterable = options.fetch(:filter) { false } @symbols = @prompt.symbols.merge(options.fetch(:symbols, {})) @quiet = options.fetch(:quiet) { @prompt.quiet } - @submit_keys = keys_with_labels(options.fetch(:submit_keys) { default_submit_keys }) + @submit_keys = init_action_keys(options.fetch(:submit_keys) { default_submit_keys }) @filter = [] @filter_cache = {} @help = options[:help] @@ -87,6 +87,41 @@ def default_submit_keys %i[space return enter].freeze end + # Ensure that if any EOL char is passed as an action key + # then all EOL chars are included (for cross-system compat) + # Maintain any custom labels. + # + # @return [Array[Hash]] + # + # @api private + def ensure_eol_compat(keys) + eol_symbols = %i[enter return].sort + key_symbols = keys.keys.sort + key_intersection = key_symbols & eol_symbols + + case key_intersection + when [], eol_symbols + keys + else + eol_label = keys[key_intersection[0]] + all_eol_keys = eol_symbols.reduce({}) { |hash, key| + hash.merge({key => eol_label}) + } + all_eol_keys.merge(keys) + end + end + + # Initialize any default or custom action keys + # setting up their labels and dealing with compat + # + # @return [Array[Hash]] + # + # @api private + def init_action_keys(keys) + keys = keys_with_labels(keys) + ensure_eol_compat(keys) + end + # Select paginator based on the current navigation key # # @return [Paginator] diff --git a/lib/tty/prompt/multi_list.rb b/lib/tty/prompt/multi_list.rb index 8ffd805..4e65335 100644 --- a/lib/tty/prompt/multi_list.rb +++ b/lib/tty/prompt/multi_list.rb @@ -19,7 +19,7 @@ class MultiList < List def initialize(prompt, **options) super @selected = SelectedChoices.new - @select_keys = keys_with_labels(options.fetch(:select_keys, [:space])) + @select_keys = init_action_keys(options.fetch(:select_keys, [:space])) @help = options[:help] @echo = options.fetch(:echo, true) @min = options[:min] diff --git a/spec/unit/select_spec.rb b/spec/unit/select_spec.rb index 7382435..7446399 100644 --- a/spec/unit/select_spec.rb +++ b/spec/unit/select_spec.rb @@ -903,6 +903,40 @@ def exit_message(prompt, choice) expect(actual_prompt_output).to eql(expected_prompt_output) end + it "filters and chooses entry with enter even if only return is passed in submit_keys" do + prompt.input << "g" << "\n" + prompt.input.rewind + + answer = prompt.select("What size?", %i[Small Medium Large Huge], filter: true, submit_keys: [:return]) + expect(answer).to eql(:Large) + + actual_prompt_output = prompt.output.string + expected_prompt_output = + output_helper("What size?", %w[Small Medium Large Huge], "Small", init: true, + hint: "Press #{up_down} arrow to move, Enter to select and letters to filter") + + output_helper("What size?", %w[Large Huge], "Large", hint: "Filter: \"g\"") + + exit_message("What size?", "Large") + + expect(actual_prompt_output).to eql(expected_prompt_output) + end + + it "filters and chooses entry with enter preserving the return key label" do + prompt.input << "g" << "\n" + prompt.input.rewind + + answer = prompt.select("What size?", %i[Small Medium Large Huge], filter: true, submit_keys: [{return: ">ENTER<"}]) + expect(answer).to eql(:Large) + + actual_prompt_output = prompt.output.string + expected_prompt_output = + output_helper("What size?", %w[Small Medium Large Huge], "Small", init: true, + hint: "Press #{up_down} arrow to move, >ENTER< to select and letters to filter") + + output_helper("What size?", %w[Large Huge], "Large", hint: "Filter: \"g\"") + + exit_message("What size?", "Large") + + expect(actual_prompt_output).to eql(expected_prompt_output) + end + it "filters and chooses entry with return and enter, with default key config" do prompt.input << "g" << "\r\n" prompt.input.rewind From 1f1bc739e4230a69584153660a351e7eab53ca7b Mon Sep 17 00:00:00 2001 From: Eleni Lixourioti Date: Tue, 18 May 2021 15:11:59 +0200 Subject: [PATCH 12/33] Rename clashing to conflicting and return exact conflict --- lib/tty/prompt/multi_list.rb | 11 ++++++----- spec/unit/multi_select_spec.rb | 4 ++-- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/lib/tty/prompt/multi_list.rb b/lib/tty/prompt/multi_list.rb index 4e65335..103e618 100644 --- a/lib/tty/prompt/multi_list.rb +++ b/lib/tty/prompt/multi_list.rb @@ -25,7 +25,7 @@ def initialize(prompt, **options) @min = options[:min] @max = options[:max] - check_clashing_keys + check_conflicting_keys end # Set a minimum number of choices @@ -112,12 +112,13 @@ def keyctrl_r(*) # Checks that there are no key options clashing # # @api private - def check_clashing_keys - return if (@submit_keys.keys & @select_keys.keys).empty? + def check_conflicting_keys + conflicting_keys = @submit_keys.keys & @select_keys.keys + return if conflicting_keys.empty? raise ConfigurationError, - ":submit_keys #{@submit_keys.keys} are clashing with " \ - ":select_keys #{@select_keys.keys}" + ":submit_keys #{conflicting_keys} are conflicting with " \ + "the same keys in :select_keys" end # Setup default options and active selection diff --git a/spec/unit/multi_select_spec.rb b/spec/unit/multi_select_spec.rb index e369863..b8ed325 100644 --- a/spec/unit/multi_select_spec.rb +++ b/spec/unit/multi_select_spec.rb @@ -287,7 +287,7 @@ def exit_message(prompt, choices) expect { prompt.multi_select("Select drinks?", %w[vodka beer wine], submit_keys: [:space]) }.to raise_error(TTY::Prompt::ConfigurationError, - ":submit_keys [:space] are clashing with :select_keys [:space]") + ":submit_keys [:space] are conflicting with the same keys in :select_keys") end it "raises error when submit and select keys clash (configured)" do @@ -296,7 +296,7 @@ def exit_message(prompt, choices) expect { prompt.multi_select("Select drinks?", %w[vodka beer wine], submit_keys: %i[space ctrl_s], select_keys: [:space]) }.to raise_error(TTY::Prompt::ConfigurationError, - ":submit_keys [:space, :ctrl_s] are clashing with :select_keys [:space]") + ":submit_keys [:space] are conflicting with the same keys in :select_keys") end From 0dd68912c6c0855a131f9e72a55bec3a8d2ebb25 Mon Sep 17 00:00:00 2001 From: Eleni Lixourioti Date: Tue, 18 May 2021 15:14:28 +0200 Subject: [PATCH 13/33] Fix multi-line block formatting and simplify implementation --- lib/tty/prompt/list.rb | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/lib/tty/prompt/list.rb b/lib/tty/prompt/list.rb index a784ad9..287262a 100644 --- a/lib/tty/prompt/list.rb +++ b/lib/tty/prompt/list.rb @@ -213,13 +213,10 @@ def arrows_help # # @api private def keys_with_labels(keys) - keys.reduce({}) { |result, key| - if key.is_a?(Hash) - result.merge(key) - else - result.merge({key => key_help_label(key)}) - end - } + keys.reduce({}) do |result, key| + obj = key.is_a?(::Hash) ? key : {key => key_help_label(key)} + result.merge(obj) + end end # Convert a key name into a human-readable label From 3df157eca529fdd5407fe9f0f4c801681865e545 Mon Sep 17 00:00:00 2001 From: Eleni Lixourioti Date: Tue, 18 May 2021 15:16:57 +0200 Subject: [PATCH 14/33] Improve implementation of ensure_eol_compat --- lib/tty/prompt/list.rb | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/lib/tty/prompt/list.rb b/lib/tty/prompt/list.rb index 287262a..31e8876 100644 --- a/lib/tty/prompt/list.rb +++ b/lib/tty/prompt/list.rb @@ -95,19 +95,17 @@ def default_submit_keys # # @api private def ensure_eol_compat(keys) - eol_symbols = %i[enter return].sort + eol_symbols = %i[enter return] key_symbols = keys.keys.sort - key_intersection = key_symbols & eol_symbols + key_intersection = eol_symbols & key_symbols case key_intersection when [], eol_symbols keys else - eol_label = keys[key_intersection[0]] - all_eol_keys = eol_symbols.reduce({}) { |hash, key| - hash.merge({key => eol_label}) - } - all_eol_keys.merge(keys) + eol_label = keys[key_intersection.first] + missing_key = (eol_symbols - key_intersection).first + keys.merge({missing_key => eol_label}) end end From e1b759d0c48565c329f2e9c1517aaa7fe1b2bd99 Mon Sep 17 00:00:00 2001 From: Eleni Lixourioti Date: Tue, 18 May 2021 15:29:30 +0200 Subject: [PATCH 15/33] Add examples and fix param/return types --- lib/tty/prompt/list.rb | 37 +++++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/lib/tty/prompt/list.rb b/lib/tty/prompt/list.rb index 31e8876..6c093c8 100644 --- a/lib/tty/prompt/list.rb +++ b/lib/tty/prompt/list.rb @@ -91,7 +91,14 @@ def default_submit_keys # then all EOL chars are included (for cross-system compat) # Maintain any custom labels. # - # @return [Array[Hash]] + # @example + # keys = {return: "Enter", ctrl_s: "Ctrl+S"} + # ensure_eol_compat(keys) + # # => {enter: "Enter", return: "Enter", ctrl_s: "Ctrl+S"} + # + # @param [Hash{Symbol => String}] + # + # @return [Hash{Symbol => String}] # # @api private def ensure_eol_compat(keys) @@ -112,7 +119,9 @@ def ensure_eol_compat(keys) # Initialize any default or custom action keys # setting up their labels and dealing with compat # - # @return [Array[Hash]] + # @param [Array String}>] + # + # @return [Hash{Symbol => String}] # # @api private def init_action_keys(keys) @@ -207,6 +216,18 @@ def arrows_help # Normalize a list of key symbols or symbol-label hashes # into a single symbol-label lookup hash. # + # @example Only with symbol keys + # keys = [:enter, :ctrl_s] + # keys_with_labels(keys) + # # => {enter: "Enter", ctrl_s: "Ctrl+S"} + # + # @example With mixed keys + # keys = [:enter, {ctrl_s: "Ctrl-S"}] + # keys_with_labels(keys) + # # => {enter: "Enter", ctrl_s: "Ctrl-S"} + # + # @param [Array String}>] + # # @return [String] # # @api private @@ -232,6 +253,18 @@ def key_help_label(key_name) # Information about keys that submit the selection # + # @example Get help string for many keys + # keys = {return: "Enter", ctrl_s: "Ctrl+S", space: "Space"} + # keys_help(keys) + # # => "Enter, Ctrl+S or Space" + # + # @example Get help string for one key + # keys = {return: "Enter"} + # keys_help(keys) + # # => "Enter" + # + # @param [Hash{Symbol => String}] + # # @return [String] # # @api private From 94e79a59d0857957b094fdc3005a1a2434237b7b Mon Sep 17 00:00:00 2001 From: Eleni Lixourioti Date: Tue, 18 May 2021 15:42:28 +0200 Subject: [PATCH 16/33] Reorder key-related functions in the same cluster --- lib/tty/prompt/list.rb | 104 +++++++++++++++++++++-------------------- 1 file changed, 53 insertions(+), 51 deletions(-) diff --git a/lib/tty/prompt/list.rb b/lib/tty/prompt/list.rb index 6c093c8..7ad86a1 100644 --- a/lib/tty/prompt/list.rb +++ b/lib/tty/prompt/list.rb @@ -87,6 +87,59 @@ def default_submit_keys %i[space return enter].freeze end + # Initialize any default or custom action keys + # setting up their labels and dealing with compat + # + # @param [Array String}>] + # + # @return [Hash{Symbol => String}] + # + # @api private + def init_action_keys(keys) + keys = keys_with_labels(keys) + ensure_eol_compat(keys) + end + + # Normalize a list of key symbols or symbol-label hashes + # into a single symbol-label lookup hash. + # + # @example Only with symbol keys + # keys = [:enter, :ctrl_s] + # keys_with_labels(keys) + # # => {enter: "Enter", ctrl_s: "Ctrl+S"} + # + # @example With mixed keys + # keys = [:enter, {ctrl_s: "Ctrl-S"}] + # keys_with_labels(keys) + # # => {enter: "Enter", ctrl_s: "Ctrl-S"} + # + # @param [Array String}>] + # + # @return [String] + # + # @api private + def keys_with_labels(keys) + keys.reduce({}) do |result, key| + obj = key.is_a?(::Hash) ? key : {key => key_help_label(key)} + result.merge(obj) + end + end + + # Convert a key name into a human-readable label + # + # @param [Symbol] + # + # @return [String] + # + # @api private + def key_help_label(key_name) + if key_name == :return + "Enter" + else + key_name.to_s.split("_").map(&:capitalize).join("+") + end + end + # Ensure that if any EOL char is passed as an action key # then all EOL chars are included (for cross-system compat) # Maintain any custom labels. @@ -116,19 +169,6 @@ def ensure_eol_compat(keys) end end - # Initialize any default or custom action keys - # setting up their labels and dealing with compat - # - # @param [Array String}>] - # - # @return [Hash{Symbol => String}] - # - # @api private - def init_action_keys(keys) - keys = keys_with_labels(keys) - ensure_eol_compat(keys) - end - # Select paginator based on the current navigation key # # @return [Paginator] @@ -213,44 +253,6 @@ def arrows_help arrows.join end - # Normalize a list of key symbols or symbol-label hashes - # into a single symbol-label lookup hash. - # - # @example Only with symbol keys - # keys = [:enter, :ctrl_s] - # keys_with_labels(keys) - # # => {enter: "Enter", ctrl_s: "Ctrl+S"} - # - # @example With mixed keys - # keys = [:enter, {ctrl_s: "Ctrl-S"}] - # keys_with_labels(keys) - # # => {enter: "Enter", ctrl_s: "Ctrl-S"} - # - # @param [Array String}>] - # - # @return [String] - # - # @api private - def keys_with_labels(keys) - keys.reduce({}) do |result, key| - obj = key.is_a?(::Hash) ? key : {key => key_help_label(key)} - result.merge(obj) - end - end - - # Convert a key name into a human-readable label - # - # @return [String] - # - # @api private - def key_help_label(key_name) - if key_name == :return - "Enter" - else - key_name.to_s.split("_").map(&:capitalize).join("+") - end - end - # Information about keys that submit the selection # # @example Get help string for many keys From 1bcba3f01539614401538717931c415669b8c635 Mon Sep 17 00:00:00 2001 From: Eleni Lixourioti Date: Sun, 6 Jun 2021 09:20:32 +0200 Subject: [PATCH 17/33] Set next version to unreleased and fix contributor name --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 29c8de2..6a0ead6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Change log -## [v0.24.0] - 2021-??-?? +## [v0.24.0] - unreleased ### Changed * Change #select and #multi_select to accept a :submit_keys option, instead of always defaulting to space and return by Eleni Lixourioti(@Geekfish) @@ -8,7 +8,7 @@ ### Fixed * Fix #select and #multi_select hint to print out the correct submit_keys by Eleni Lixourioti(@Geekfish) -* Fix typo in #slider show_help(@Geekfish) +* Fix typo in #slider show_help by Eleni Lixourioti(@Geekfish) ## [v0.23.1] - 2021-04-17 From fd1cbdd8b3b23b3cf3a7b2dfc0baac5c12060753 Mon Sep 17 00:00:00 2001 From: Eleni Lixourioti Date: Sun, 6 Jun 2021 10:33:20 +0200 Subject: [PATCH 18/33] Rename :submit_keys to :confirm_keys --- CHANGELOG.md | 4 ++-- README.md | 20 +++++++++---------- examples/multi_select_custom_keys.rb | 2 +- ...labels.rb => select_confirm_key_labels.rb} | 2 +- examples/select_filtered_with_spaces.rb | 2 +- lib/tty/prompt/list.rb | 18 ++++++++--------- lib/tty/prompt/multi_list.rb | 14 ++++++------- spec/unit/multi_select_spec.rb | 16 +++++++-------- spec/unit/select_spec.rb | 16 +++++++-------- 9 files changed, 47 insertions(+), 47 deletions(-) rename examples/{select_submit_key_labels.rb => select_confirm_key_labels.rb} (77%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a0ead6..fe5e935 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,11 +3,11 @@ ## [v0.24.0] - unreleased ### Changed -* Change #select and #multi_select to accept a :submit_keys option, instead of always defaulting to space and return by Eleni Lixourioti(@Geekfish) +* Change #select and #multi_select to accept a :confirm_keys option, instead of always defaulting to space and return by Eleni Lixourioti(@Geekfish) * Change #multi_select to accept a :select_keys option, instead of always defaulting to space by Eleni Lixourioti(@Geekfish) ### Fixed -* Fix #select and #multi_select hint to print out the correct submit_keys by Eleni Lixourioti(@Geekfish) +* Fix #select and #multi_select hint to print out the correct confirm_keys by Eleni Lixourioti(@Geekfish) * Fix typo in #slider show_help by Eleni Lixourioti(@Geekfish) ## [v0.23.1] - 2021-04-17 diff --git a/README.md b/README.md index 27d62f4..af87916 100644 --- a/README.md +++ b/README.md @@ -89,7 +89,7 @@ Or install it yourself as: * [2.6.2.5 :per_page](#2625-per_page) * [2.6.2.6 :disabled](#2626-disabled) * [2.6.2.7 :filter](#2627-filter) - * [2.6.2.8 :submit_keys](#2628-submit_keys) + * [2.6.2.8 :confirm_keys](#2628-confirm_keys) * [2.6.3 multi_select](#263-multi_select) * [2.6.3.1 :cycle](#2631-cycle) * [2.6.3.2 :enum](#2632-enum) @@ -100,7 +100,7 @@ Or install it yourself as: * [2.6.3.7 :filter](#2637-filter) * [2.6.3.8 :min](#2638-min) * [2.6.3.9 :max](#2639-max) - * [2.6.3.10 :submit_keys](#26310-submit_keys) + * [2.6.3.10 :confirm_keys](#26310-confirm_keys) * [2.6.3.11 :select_keys](#262311-select_keys) * [2.6.4 enum_select](#264-enum_select) * [2.6.4.1 :per_page](#2641-per_page) @@ -919,13 +919,13 @@ Filter characters can be deleted partially or entirely via, respectively, Backsp If the user changes or deletes a filter, the choices previously selected remain selected. -#### 2.6.2.8 `:submit_keys` +#### 2.6.2.8 `:confirm_keys` You can configure which key(s) submit your selection (`:space` and `:return` by default): ```ruby choices = %w(Scorpion Kano Jax) -prompt.select("Choose your destiny?", choices, submit_keys: [:ctrl_s]) +prompt.select("Choose your destiny?", choices, confirm_keys: [:ctrl_s]) # => # Choose your destiny? (Press ↑/↓ to move and Ctrl+S to select) # ‣ Scorpion @@ -937,7 +937,7 @@ This is particularly useful in conjunction with `:filter`, as you may have choic ```ruby choices = ["Jax Sr", "Jax", "Jax Jr"] -prompt.select("Choose your destiny?", choices, submit_keys: [:ctrl_s], filter: true) +prompt.select("Choose your destiny?", choices, confirm_keys: [:ctrl_s], filter: true) # => # Choose your destiny? (Press ↑/↓ to move, Ctrl+S to select and letters to filter) # ‣ Jax Sr @@ -967,7 +967,7 @@ You may also pass your own key labels to be displayed in the hint: ```ruby choices = ["Jax Sr", "Jax", "Jax Jr"] -prompt.select("Choose your destiny?", choices, submit_keys: [:enter, {escape: "ESC"}]) +prompt.select("Choose your destiny?", choices, confirm_keys: [:enter, {escape: "ESC"}]) # => # Choose your destiny? (Press ↑/↓ to move and Enter or ESC to select) # ‣ Jax Sr @@ -1214,14 +1214,14 @@ prompt.multi_select("Select drinks?", choices, max: 3) # ‣ ⬡ bourbon ``` -#### 2.6.3.10 `:submit_keys` +#### 2.6.3.10 `:confirm_keys` -This works similar to the [same option for `select`](#2628-submit_keys). +This works similar to the [same option for `select`](#2628-confirm_keys). You can configure which key(s) submit your selection (`:return` by default): ```ruby choices = %w(vodka beer wine whisky bourbon) -prompt.multi_select("Select drinks?", choices, submit_keys: [:ctrl_s]) +prompt.multi_select("Select drinks?", choices, confirm_keys: [:ctrl_s]) # => # Select drinks? vodka, beer, whisky # ⬢ vodka @@ -1282,7 +1282,7 @@ The user can then press the `Ctrl+S` key combo to select the options: # ⬡ gin fizz ``` -Similar to the `:submit_keys` option, you may also pass your own key labels to be displayed in the hint: +Similar to the `:confirm_keys` option, you may also pass your own key labels to be displayed in the hint: ```ruby choices = ["gin", "gin tonic", "gin fizz", "beer"] diff --git a/examples/multi_select_custom_keys.rb b/examples/multi_select_custom_keys.rb index ad12b9c..8730bd9 100644 --- a/examples/multi_select_custom_keys.rb +++ b/examples/multi_select_custom_keys.rb @@ -6,5 +6,5 @@ drinks = %w[vodka beer wine whisky bourbon] prompt.multi_select("Choose your favourite drink?", drinks, - submit_keys: [:return, {escape: "Esc"}], + confirm_keys: [:return, {escape: "Esc"}], select_keys: [{space: "Spacebar"}, :ctrl_s]) diff --git a/examples/select_submit_key_labels.rb b/examples/select_confirm_key_labels.rb similarity index 77% rename from examples/select_submit_key_labels.rb rename to examples/select_confirm_key_labels.rb index b362e8f..95a5c96 100644 --- a/examples/select_submit_key_labels.rb +++ b/examples/select_confirm_key_labels.rb @@ -7,6 +7,6 @@ warriors = %w[Scorpion Kano Jax Kitana Raiden] answer = prompt.select("Choose your destiny?", warriors, - submit_keys: [:enter, {escape: "ESC"}]) + confirm_keys: [:enter, {escape: "ESC"}]) puts answer.inspect \ No newline at end of file diff --git a/examples/select_filtered_with_spaces.rb b/examples/select_filtered_with_spaces.rb index 2129a31..92325c0 100644 --- a/examples/select_filtered_with_spaces.rb +++ b/examples/select_filtered_with_spaces.rb @@ -7,6 +7,6 @@ warriors = ["Jax", "Jax Jr", "Kitana", "Raiden ft. Thunder"] answer = prompt.select("Choose your destiny?", warriors, - filter: true, submit_keys: %i[return ctrl_s]) + filter: true, confirm_keys: %i[return ctrl_s]) puts answer.inspect diff --git a/lib/tty/prompt/list.rb b/lib/tty/prompt/list.rb index 7ad86a1..32f3db1 100644 --- a/lib/tty/prompt/list.rb +++ b/lib/tty/prompt/list.rb @@ -47,7 +47,7 @@ def initialize(prompt, **options) @filterable = options.fetch(:filter) { false } @symbols = @prompt.symbols.merge(options.fetch(:symbols, {})) @quiet = options.fetch(:quiet) { @prompt.quiet } - @submit_keys = init_action_keys(options.fetch(:submit_keys) { default_submit_keys }) + @confirm_keys = init_action_keys(options.fetch(:confirm_keys) { default_confirm_keys }) @filter = [] @filter_cache = {} @help = options[:help] @@ -80,10 +80,10 @@ def default(*default_values) @default = default_values end - # Default submit keys, if not explicitly configured + # Default confirm keys, if not explicitly configured # # @api private - def default_submit_keys + def default_confirm_keys %i[space return enter].freeze end @@ -253,7 +253,7 @@ def arrows_help arrows.join end - # Information about keys that submit the selection + # Information about keys that confirm the selection # # @example Get help string for many keys # keys = {return: "Enter", ctrl_s: "Ctrl+S", space: "Space"} @@ -291,7 +291,7 @@ def default_help str << " or 1-#{choices.size} number" if enumerate? str << " to move" str << (filterable? ? "," : " and") - str << " #{keys_help(@submit_keys)} to select" + str << " #{keys_help(@confirm_keys)} to select" str << " and letters to filter" if filterable? str << ")" str.join @@ -457,16 +457,16 @@ def keyleft(*) end alias keypage_up keyleft - # Callback fired when a submit key is pressed + # Callback fired when a confirm key is pressed # # @api private - def submit + def confirm @done = true unless choices.empty? end def keypress(event) - if @submit_keys.include?(event.key.name) - submit + if @confirm_keys.include?(event.key.name) + confirm elsif event.key.name == :tab keydown elsif filterable? && event.value =~ FILTER_KEYS_MATCHER diff --git a/lib/tty/prompt/multi_list.rb b/lib/tty/prompt/multi_list.rb index 103e618..a998241 100644 --- a/lib/tty/prompt/multi_list.rb +++ b/lib/tty/prompt/multi_list.rb @@ -42,17 +42,17 @@ def max(value) @max = value end - # Default submit keys, if not explicitly configured + # Default confirm keys, if not explicitly configured # # @api private - def default_submit_keys + def default_confirm_keys %i[return enter].freeze end - # Callback fired when a submit key is pressed + # Callback fired when a confirm key is pressed # # @api private - def submit + def confirm valid = true valid = @min <= @selected.size if @min valid = @selected.size <= @max if @max @@ -113,11 +113,11 @@ def keyctrl_r(*) # # @api private def check_conflicting_keys - conflicting_keys = @submit_keys.keys & @select_keys.keys + conflicting_keys = @confirm_keys.keys & @select_keys.keys return if conflicting_keys.empty? raise ConfigurationError, - ":submit_keys #{conflicting_keys} are conflicting with " \ + ":confirm_keys #{conflicting_keys} are conflicting with " \ "the same keys in :select_keys" end @@ -183,7 +183,7 @@ def default_help str << " to select" str << " (all|rev)" if @max.nil? str << (filterable? ? "," : " and") - str << " #{keys_help(@submit_keys)} to finish" + str << " #{keys_help(@confirm_keys)} to finish" str << " and letters to filter" if filterable? str << ")" str.join diff --git a/spec/unit/multi_select_spec.rb b/spec/unit/multi_select_spec.rb index b8ed325..79a87a7 100644 --- a/spec/unit/multi_select_spec.rb +++ b/spec/unit/multi_select_spec.rb @@ -99,11 +99,11 @@ def exit_message(prompt, choices) expect(prompt.output.string).to eq(expected_output) end - it "selects item and submits selection with custom keys" do + it "selects item and confirms selection with custom keys" do choices = %w[vodka beer wine whisky bourbon] prompt.input << "\C-s\e" prompt.input.rewind - expect(prompt.multi_select("Select drinks?", choices, select_keys: [:ctrl_s], submit_keys: [{escape: "Esc"}])).to eq(["vodka"]) + expect(prompt.multi_select("Select drinks?", choices, select_keys: [:ctrl_s], confirm_keys: [{escape: "Esc"}])).to eq(["vodka"]) expected_output = output_helper("Select drinks?", choices, "vodka", [], init: true, @@ -281,22 +281,22 @@ def exit_message(prompt, choices) /default index `6` out of range \(1 - 5\)/) end - it "raises error when submit and select keys clash (with default select_key)" do + it "raises error when confirm and select keys clash (with default select_key)" do prompt.input << "\r" prompt.input.rewind expect { - prompt.multi_select("Select drinks?", %w[vodka beer wine], submit_keys: [:space]) + prompt.multi_select("Select drinks?", %w[vodka beer wine], confirm_keys: [:space]) }.to raise_error(TTY::Prompt::ConfigurationError, - ":submit_keys [:space] are conflicting with the same keys in :select_keys") + ":confirm_keys [:space] are conflicting with the same keys in :select_keys") end - it "raises error when submit and select keys clash (configured)" do + it "raises error when confirm and select keys clash (configured)" do prompt.input << "\r" prompt.input.rewind expect { - prompt.multi_select("Select drinks?", %w[vodka beer wine], submit_keys: %i[space ctrl_s], select_keys: [:space]) + prompt.multi_select("Select drinks?", %w[vodka beer wine], confirm_keys: %i[space ctrl_s], select_keys: [:space]) }.to raise_error(TTY::Prompt::ConfigurationError, - ":submit_keys [:space] are conflicting with the same keys in :select_keys") + ":confirm_keys [:space] are conflicting with the same keys in :select_keys") end diff --git a/spec/unit/select_spec.rb b/spec/unit/select_spec.rb index 7446399..e01c3ec 100644 --- a/spec/unit/select_spec.rb +++ b/spec/unit/select_spec.rb @@ -903,11 +903,11 @@ def exit_message(prompt, choice) expect(actual_prompt_output).to eql(expected_prompt_output) end - it "filters and chooses entry with enter even if only return is passed in submit_keys" do + it "filters and chooses entry with enter even if only return is passed in confirm_keys" do prompt.input << "g" << "\n" prompt.input.rewind - answer = prompt.select("What size?", %i[Small Medium Large Huge], filter: true, submit_keys: [:return]) + answer = prompt.select("What size?", %i[Small Medium Large Huge], filter: true, confirm_keys: [:return]) expect(answer).to eql(:Large) actual_prompt_output = prompt.output.string @@ -924,7 +924,7 @@ def exit_message(prompt, choice) prompt.input << "g" << "\n" prompt.input.rewind - answer = prompt.select("What size?", %i[Small Medium Large Huge], filter: true, submit_keys: [{return: ">ENTER<"}]) + answer = prompt.select("What size?", %i[Small Medium Large Huge], filter: true, confirm_keys: [{return: ">ENTER<"}]) expect(answer).to eql(:Large) actual_prompt_output = prompt.output.string @@ -971,11 +971,11 @@ def exit_message(prompt, choice) expect(actual_prompt_output).to eql(expected_prompt_output) end - it "filters and chooses entry with Ctrl+S when configured as submit key" do + it "filters and chooses entry with Ctrl+S when configured as confirm key" do prompt.input << "g" << "\C-s" prompt.input.rewind - answer = prompt.select("What size?", %i[Small Medium Large Huge], filter: true, submit_keys: [:ctrl_s]) + answer = prompt.select("What size?", %i[Small Medium Large Huge], filter: true, confirm_keys: [:ctrl_s]) expect(answer).to eql(:Large) actual_prompt_output = prompt.output.string @@ -988,11 +988,11 @@ def exit_message(prompt, choice) expect(actual_prompt_output).to eql(expected_prompt_output) end - it "filters and chooses entry with Escape and custom label when configured as submit key" do + it "filters and chooses entry with Escape and custom label when configured as confirm key" do prompt.input << "g" << "\e" prompt.input.rewind - answer = prompt.select("What size?", %i[Small Medium Large Huge], filter: true, submit_keys: [escape: "Esc"]) + answer = prompt.select("What size?", %i[Small Medium Large Huge], filter: true, confirm_keys: [escape: "Esc"]) expect(answer).to eql(:Large) actual_prompt_output = prompt.output.string @@ -1011,7 +1011,7 @@ def exit_message(prompt, choice) choices = ["X Large", "X X Large"] - answer = prompt.select("What size?", choices, filter: true, submit_keys: [:return]) + answer = prompt.select("What size?", choices, filter: true, confirm_keys: [:return]) expect(answer).to eql("X X Large") actual_prompt_output = prompt.output.string From e8bc20014ed4778d3ad13d0ecaecc45132d8f9cb Mon Sep 17 00:00:00 2001 From: Eleni Lixourioti Date: Sun, 6 Jun 2021 12:21:00 +0200 Subject: [PATCH 19/33] Add support for non-special characters (incl. alphanumeric and symbols) --- README.md | 22 ++++++++-------------- examples/multi_select_custom_keys.rb | 4 ++-- examples/select_confirm_key_labels.rb | 2 +- lib/tty/prompt/list.rb | 18 +++++++++--------- lib/tty/prompt/multi_list.rb | 6 +++--- spec/unit/select_spec.rb | 17 +++++++++++++++++ 6 files changed, 40 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index af87916..fcc156b 100644 --- a/README.md +++ b/README.md @@ -921,13 +921,13 @@ If the user changes or deletes a filter, the choices previously selected remain #### 2.6.2.8 `:confirm_keys` -You can configure which key(s) submit your selection (`:space` and `:return` by default): +You can configure which key(s) confirm your selection (`:space` and `:return` by default): ```ruby choices = %w(Scorpion Kano Jax) -prompt.select("Choose your destiny?", choices, confirm_keys: [:ctrl_s]) +prompt.select("Choose your destiny?", choices, confirm_keys: [:ctrl_s, ","]) # => -# Choose your destiny? (Press ↑/↓ to move and Ctrl+S to select) +# Choose your destiny? (Press ↑/↓ to move and Ctrl+S or , to select) # ‣ Scorpion # Kano # Jax @@ -967,16 +967,14 @@ You may also pass your own key labels to be displayed in the hint: ```ruby choices = ["Jax Sr", "Jax", "Jax Jr"] -prompt.select("Choose your destiny?", choices, confirm_keys: [:enter, {escape: "ESC"}]) +prompt.select("Choose your destiny?", choices, confirm_keys: [:enter, {escape: "ESC"}, {"," => "Comma (,)"}]) # => -# Choose your destiny? (Press ↑/↓ to move and Enter or ESC to select) +# Choose your destiny? (Press ↑/↓ to move and Enter, ESC or Comma (,) to select) # ‣ Jax Sr # Jax # Jax Jr ``` -Please note that alphanumeric keys are *not* supported. - ### 2.6.3 multi_select For asking questions involving multiple selection list use `multi_select` method by passing the question and possible choices: @@ -1217,11 +1215,11 @@ prompt.multi_select("Select drinks?", choices, max: 3) #### 2.6.3.10 `:confirm_keys` This works similar to the [same option for `select`](#2628-confirm_keys). -You can configure which key(s) submit your selection (`:return` by default): +You can configure which key(s) confirm your selection (`:return` by default): ```ruby choices = %w(vodka beer wine whisky bourbon) -prompt.multi_select("Select drinks?", choices, confirm_keys: [:ctrl_s]) +prompt.multi_select("Select drinks?", choices, confirm_keys: [:ctrl_s, ","]) # => # Select drinks? vodka, beer, whisky # ⬢ vodka @@ -1231,14 +1229,12 @@ prompt.multi_select("Select drinks?", choices, confirm_keys: [:ctrl_s]) # ‣ ⬡ bourbon ``` -The user can then press the `Ctrl+S` key to confirm their selection: +The user can then press the `Ctrl+S` or the `,` key to confirm their selection: ```ruby # => # Select drinks? vodka, beer, whisky ``` -Please note that alphanumeric keys are *not* supported. - #### 2.6.3.11 `:select_keys` You can configure which key selects an option (`:space` default). @@ -1295,8 +1291,6 @@ prompt.multi_select("Select drinks?", choices, filter: true, select_keys: [{ctr_ # ⬡ beer ``` -Please note that alphanumeric keys are *not* supported. - ### 2.6.4 enum_select In order to ask for standard selection from indexed list you can use `enum_select` and pass question together with possible choices: diff --git a/examples/multi_select_custom_keys.rb b/examples/multi_select_custom_keys.rb index 8730bd9..35a95ec 100644 --- a/examples/multi_select_custom_keys.rb +++ b/examples/multi_select_custom_keys.rb @@ -6,5 +6,5 @@ drinks = %w[vodka beer wine whisky bourbon] prompt.multi_select("Choose your favourite drink?", drinks, - confirm_keys: [:return, {escape: "Esc"}], - select_keys: [{space: "Spacebar"}, :ctrl_s]) + confirm_keys: [:return, {escape: "Esc"}, "."], + select_keys: [{space: "Spacebar"}, :ctrl_s, ","]) diff --git a/examples/select_confirm_key_labels.rb b/examples/select_confirm_key_labels.rb index 95a5c96..9677133 100644 --- a/examples/select_confirm_key_labels.rb +++ b/examples/select_confirm_key_labels.rb @@ -7,6 +7,6 @@ warriors = %w[Scorpion Kano Jax Kitana Raiden] answer = prompt.select("Choose your destiny?", warriors, - confirm_keys: [:enter, {escape: "ESC"}]) + confirm_keys: [:enter, {escape: "ESC"}, ","]) puts answer.inspect \ No newline at end of file diff --git a/lib/tty/prompt/list.rb b/lib/tty/prompt/list.rb index 32f3db1..330aead 100644 --- a/lib/tty/prompt/list.rb +++ b/lib/tty/prompt/list.rb @@ -90,9 +90,9 @@ def default_confirm_keys # Initialize any default or custom action keys # setting up their labels and dealing with compat # - # @param [Array String}>] + # @param [Array String}>] # - # @return [Hash{Symbol => String}] + # @return [Hash{Symbol, String => String}] # # @api private def init_action_keys(keys) @@ -113,7 +113,7 @@ def init_action_keys(keys) # keys_with_labels(keys) # # => {enter: "Enter", ctrl_s: "Ctrl-S"} # - # @param [Array String}>] + # @param [Array String}>] # # @return [String] # @@ -127,7 +127,7 @@ def keys_with_labels(keys) # Convert a key name into a human-readable label # - # @param [Symbol] + # @param [Symbol, String] # # @return [String] # @@ -149,14 +149,14 @@ def key_help_label(key_name) # ensure_eol_compat(keys) # # => {enter: "Enter", return: "Enter", ctrl_s: "Ctrl+S"} # - # @param [Hash{Symbol => String}] + # @param [Hash{Symbol, String => String}] # - # @return [Hash{Symbol => String}] + # @return [Hash{Symbol, String => String}] # # @api private def ensure_eol_compat(keys) eol_symbols = %i[enter return] - key_symbols = keys.keys.sort + key_symbols = keys.keys.sort_by(&:to_s) key_intersection = eol_symbols & key_symbols case key_intersection @@ -265,7 +265,7 @@ def arrows_help # keys_help(keys) # # => "Enter" # - # @param [Hash{Symbol => String}] + # @param [Hash{Symbol, String => String}] # # @return [String] # @@ -465,7 +465,7 @@ def confirm end def keypress(event) - if @confirm_keys.include?(event.key.name) + if !(@confirm_keys.keys & [event.key.name, event.value]).empty? confirm elsif event.key.name == :tab keydown diff --git a/lib/tty/prompt/multi_list.rb b/lib/tty/prompt/multi_list.rb index a998241..fa89ddd 100644 --- a/lib/tty/prompt/multi_list.rb +++ b/lib/tty/prompt/multi_list.rb @@ -78,10 +78,10 @@ def select_choice # # @api private def keypress(event) - if @select_keys.include?(event.key.name) - select_choice - else + if (@select_keys.keys & [event.key.name, event.value]).empty? super(event) + else + select_choice end end diff --git a/spec/unit/select_spec.rb b/spec/unit/select_spec.rb index e01c3ec..a985cb9 100644 --- a/spec/unit/select_spec.rb +++ b/spec/unit/select_spec.rb @@ -1005,6 +1005,23 @@ def exit_message(prompt, choice) expect(actual_prompt_output).to eql(expected_prompt_output) end + it "filters and chooses entry with , and custom label when configured as confirm key" do + prompt.input << "g" << "," + prompt.input.rewind + + answer = prompt.select("What size?", %i[Small Medium Large Huge], filter: true, confirm_keys: [{escape: "Esc"}, {"," => "Comma (,)"}]) + expect(answer).to eql(:Large) + + actual_prompt_output = prompt.output.string + expected_prompt_output = + output_helper("What size?", %w[Small Medium Large Huge], "Small", init: true, + hint: "Press #{up_down} arrow to move, Esc or Comma (,) to select and letters to filter") + + output_helper("What size?", %w[Large Huge], "Large", hint: "Filter: \"g\"") + + exit_message("What size?", "Large") + + expect(actual_prompt_output).to eql(expected_prompt_output) + end + it "does not choose entry with space, when configured to use only enter" do prompt.input << "X" << " " << "X" << "\r" prompt.input.rewind From d3ae0f8c43c6a66a6a0c6ba6023f3594dba4cd31 Mon Sep 17 00:00:00 2001 From: Eleni Lixourioti Date: Tue, 20 Jul 2021 13:55:02 +0200 Subject: [PATCH 20/33] Make default confirm keys for select a constant --- lib/tty/prompt/list.rb | 12 ++++-------- lib/tty/prompt/multi_list.rb | 10 +++------- 2 files changed, 7 insertions(+), 15 deletions(-) diff --git a/lib/tty/prompt/list.rb b/lib/tty/prompt/list.rb index 330aead..e5356b4 100644 --- a/lib/tty/prompt/list.rb +++ b/lib/tty/prompt/list.rb @@ -19,6 +19,9 @@ class List # Checks type of default parameter to be integer INTEGER_MATCHER = /\A\d+\Z/.freeze + # The default keys that confirm the selected item(s) + DEFAULT_CONFIRM_KEYS = %i[space return enter].freeze + # Create instance of TTY::Prompt::List menu. # # @param Hash options @@ -47,7 +50,7 @@ def initialize(prompt, **options) @filterable = options.fetch(:filter) { false } @symbols = @prompt.symbols.merge(options.fetch(:symbols, {})) @quiet = options.fetch(:quiet) { @prompt.quiet } - @confirm_keys = init_action_keys(options.fetch(:confirm_keys) { default_confirm_keys }) + @confirm_keys = init_action_keys(options.fetch(:confirm_keys) { self.class::DEFAULT_CONFIRM_KEYS }) @filter = [] @filter_cache = {} @help = options[:help] @@ -80,13 +83,6 @@ def default(*default_values) @default = default_values end - # Default confirm keys, if not explicitly configured - # - # @api private - def default_confirm_keys - %i[space return enter].freeze - end - # Initialize any default or custom action keys # setting up their labels and dealing with compat # diff --git a/lib/tty/prompt/multi_list.rb b/lib/tty/prompt/multi_list.rb index fa89ddd..0b66896 100644 --- a/lib/tty/prompt/multi_list.rb +++ b/lib/tty/prompt/multi_list.rb @@ -10,6 +10,9 @@ class Prompt # # @api private class MultiList < List + # The default keys that confirm the selected item(s) + DEFAULT_CONFIRM_KEYS = %i[return enter].freeze + # Create instance of TTY::Prompt::MultiList menu. # # @param [Prompt] :prompt @@ -42,13 +45,6 @@ def max(value) @max = value end - # Default confirm keys, if not explicitly configured - # - # @api private - def default_confirm_keys - %i[return enter].freeze - end - # Callback fired when a confirm key is pressed # # @api private From b4c71b62c954910a5435a5c273d4752a8e85fa69 Mon Sep 17 00:00:00 2001 From: Eleni Lixourioti Date: Wed, 21 Jul 2021 11:06:45 +0200 Subject: [PATCH 21/33] Fix jruby-head bug which seems to ignore when statement --- lib/tty/prompt/list.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/tty/prompt/list.rb b/lib/tty/prompt/list.rb index e5356b4..e735968 100644 --- a/lib/tty/prompt/list.rb +++ b/lib/tty/prompt/list.rb @@ -155,8 +155,7 @@ def ensure_eol_compat(keys) key_symbols = keys.keys.sort_by(&:to_s) key_intersection = eol_symbols & key_symbols - case key_intersection - when [], eol_symbols + if key_intersection.empty? || key_intersection == eol_symbols keys else eol_label = keys[key_intersection.first] From bdbdd2b17914c0ac043e6898700e773ea2dd7ea5 Mon Sep 17 00:00:00 2001 From: Eleni Lixourioti Date: Fri, 30 Jul 2021 22:17:44 +0200 Subject: [PATCH 22/33] Add DSL support for confirm_keys and select_keys --- README.md | 33 +++++++++++++++++++++++ examples/multi_select_custom_keys_dsl.rb | 12 +++++++++ lib/tty/prompt/list.rb | 15 +++++++++++ lib/tty/prompt/multi_list.rb | 34 +++++++++++++++++++++--- spec/unit/multi_select_spec.rb | 23 ++++++++++++++++ spec/unit/select_spec.rb | 20 ++++++++++++++ 6 files changed, 134 insertions(+), 3 deletions(-) create mode 100644 examples/multi_select_custom_keys_dsl.rb diff --git a/README.md b/README.md index fcc156b..54cdd75 100644 --- a/README.md +++ b/README.md @@ -975,6 +975,21 @@ prompt.select("Choose your destiny?", choices, confirm_keys: [:enter, {escape: " # Jax Jr ``` +Confirm keys may be configured using the DSL, similar to `:choices`: + +```ruby +choices = ["Jax Sr", "Jax", "Jax Jr"] + +prompt.select("Choose your destiny?") do |menu| + menu.choices choices + menu.confirm_keys [:enter, {escape: "ESC"}, {"," => "Comma (,)"}] +# => +# Choose your destiny? (Press ↑/↓ to move and Enter, ESC or Comma (,) to select) +# ‣ Jax Sr +# Jax +# Jax Jr +``` + ### 2.6.3 multi_select For asking questions involving multiple selection list use `multi_select` method by passing the question and possible choices: @@ -1235,6 +1250,8 @@ The user can then press the `Ctrl+S` or the `,` key to confirm their selection: # Select drinks? vodka, beer, whisky ``` +You may also configure the keys using the DSL, in the same way that is done for [select](#2628-confirm_keys) + #### 2.6.3.11 `:select_keys` You can configure which key selects an option (`:space` default). @@ -1291,6 +1308,22 @@ prompt.multi_select("Select drinks?", choices, filter: true, select_keys: [{ctr_ # ⬡ beer ``` +You can also configure your keys using the DSL, similar to `:confirm_keys`: +```ruby +choices = ["gin", "gin tonic", "gin fizz", "beer"] +prompt.multi_select("Select drinks?") do |menu| + menu.choices choices + menu.select_keys [{ctr_s: "Ctrl-S"}, {space: "Spacebar"}] +end + +# => +# Select drinks? (Press ↑/↓ arrow keys to move, Ctrl-S or Spacebar/Ctrl+A|R to select (all|rev), and Enter to finish) +# ‣ ⬡ gin +# ⬡ gin tonic +# ⬡ gin fizz +# ⬡ beer +``` + ### 2.6.4 enum_select In order to ask for standard selection from indexed list you can use `enum_select` and pass question together with possible choices: diff --git a/examples/multi_select_custom_keys_dsl.rb b/examples/multi_select_custom_keys_dsl.rb new file mode 100644 index 0000000..be9ec7c --- /dev/null +++ b/examples/multi_select_custom_keys_dsl.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +require_relative "../lib/tty-prompt" + +prompt = TTY::Prompt.new + +drinks = %w[vodka beer wine whisky bourbon] +prompt.multi_select("Choose your favourite drink?") do |menu| + menu.choices drinks + menu.confirm_keys [:return, {escape: "Esc"}, "."] + menu.select_keys [{space: "Spacebar"}, :ctrl_s, ","] +end diff --git a/lib/tty/prompt/list.rb b/lib/tty/prompt/list.rb index e735968..921c075 100644 --- a/lib/tty/prompt/list.rb +++ b/lib/tty/prompt/list.rb @@ -83,6 +83,21 @@ def default(*default_values) @default = default_values end + # Set confirm keys + # + # @param [Array String}>] value + # the new confirm_keys + # + # @return [Hash{Symbol, String => String}] + # + # @api public + def confirm_keys(value = (not_set = true)) + return @confirm_keys if !@confirm_keys.nil? && not_set + + value = not_set ? self.class::DEFAULT_CONFIRM_KEYS : value + @confirm_keys = init_action_keys(value) + end + # Initialize any default or custom action keys # setting up their labels and dealing with compat # diff --git a/lib/tty/prompt/multi_list.rb b/lib/tty/prompt/multi_list.rb index 0b66896..8cd165a 100644 --- a/lib/tty/prompt/multi_list.rb +++ b/lib/tty/prompt/multi_list.rb @@ -12,6 +12,7 @@ class Prompt class MultiList < List # The default keys that confirm the selected item(s) DEFAULT_CONFIRM_KEYS = %i[return enter].freeze + DEFAULT_SELECT_KEYS = %i[space].freeze # Create instance of TTY::Prompt::MultiList menu. # @@ -22,13 +23,11 @@ class MultiList < List def initialize(prompt, **options) super @selected = SelectedChoices.new - @select_keys = init_action_keys(options.fetch(:select_keys, [:space])) + @select_keys = init_select_keys(options.fetch(:select_keys, DEFAULT_SELECT_KEYS)) @help = options[:help] @echo = options.fetch(:echo, true) @min = options[:min] @max = options[:max] - - check_conflicting_keys end # Set a minimum number of choices @@ -56,6 +55,27 @@ def confirm super if valid end + def confirm_keys(value = (not_set = true)) + super + check_conflicting_keys + @confirm_keys + end + + # Set select keys + # + # @param [Array String}>] value + # the new select_keys + # + # @return [Hash{Symbol, String => String}] + # + # @api public + def select_keys(value = (not_set = true)) + return @select_keys if !@select_keys.nil? && not_set + + value = not_set ? self.class::DEFAULT_SELECT_KEYS : value + @select_keys = init_select_keys(value) + end + # Callback fired when the selection key is pressed # # @api private @@ -105,6 +125,14 @@ def keyctrl_r(*) private + def init_select_keys(keys) + @select_keys = init_action_keys(keys) + + check_conflicting_keys + + @select_keys + end + # Checks that there are no key options clashing # # @api private diff --git a/spec/unit/multi_select_spec.rb b/spec/unit/multi_select_spec.rb index 79a87a7..e0b85e0 100644 --- a/spec/unit/multi_select_spec.rb +++ b/spec/unit/multi_select_spec.rb @@ -240,6 +240,29 @@ def exit_message(prompt, choices) expect(prompt.output.string).to eq(expected_output) end + it "sets confirm_keys and select_keys through DSL" do + choices = %w[vodka beer wine] + prompt.on(:keypress) { |e| prompt.trigger(:keydown) if e.value == "j" } + prompt.input << "j\r\C-s" + prompt.input.rewind + value = prompt.multi_select("Select drinks?") do |menu| + menu.choices choices + menu.confirm_keys [:ctrl_s] + menu.select_keys [:enter] + end + expect(value).to eq(["beer"]) + + expected_output = + output_helper("Select drinks?", choices, "vodka", [], init: true, + hint: "Press #{up_down} arrow to move, Enter/Ctrl+A|R to select " \ + "(all|rev) and Ctrl+S to finish") + + output_helper("Select drinks?", choices, "beer", []) + + output_helper("Select drinks?", choices, "beer", %w[beer]) + + exit_message("Select drinks?", %w[beer]) + + expect(prompt.output.string).to eq(expected_output) + end + it "sets default options through hash syntax" do prompt.input << "\r" prompt.input.rewind diff --git a/spec/unit/select_spec.rb b/spec/unit/select_spec.rb index a985cb9..dc72ba6 100644 --- a/spec/unit/select_spec.rb +++ b/spec/unit/select_spec.rb @@ -134,6 +134,26 @@ def exit_message(prompt, choice) ].join) end + it "sets confirm_keys through DSL" do + choices = %w[Large Medium Small] + prompt.input << "\C-s" + prompt.input.rewind + value = prompt.select("What size?") do |menu| + menu.choices choices + menu.confirm_keys [:ctrl_s] + end + expect(value).to eq("Large") + expect(prompt.output.string).to eq([ + "\e[?25lWhat size? \e[90m(Press #{up_down} arrow to move and Ctrl+S to select)\e[0m\n", + "\e[32m#{symbols[:marker]} Large\e[0m\n", + " Medium\n", + " Small", + "\e[2K\e[1G\e[1A" * 3, + "\e[2K\e[1G", + "What size? \e[32mLarge\e[0m\n\e[?25h" + ].join) + end + it "sets choice name & value through DSL" do prompt = TTY::Prompt::Test.new(symbols: {marker: ">"}) prompt.input << " " From 5ec712fc35f6962104826e03e58321a377733997 Mon Sep 17 00:00:00 2001 From: Eleni Lixourioti Date: Sun, 1 Aug 2021 21:39:04 +0200 Subject: [PATCH 23/33] Apply code review suggestions to support splat operator arguments --- README.md | 5 +++-- examples/multi_select_custom_keys_dsl.rb | 4 ++-- lib/tty/prompt/list.rb | 16 +++++++++------- lib/tty/prompt/multi_list.rb | 24 +++++++++++++++--------- spec/unit/multi_select_spec.rb | 4 ++-- spec/unit/select_spec.rb | 2 +- 6 files changed, 32 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 54cdd75..7e0b7d2 100644 --- a/README.md +++ b/README.md @@ -982,7 +982,8 @@ choices = ["Jax Sr", "Jax", "Jax Jr"] prompt.select("Choose your destiny?") do |menu| menu.choices choices - menu.confirm_keys [:enter, {escape: "ESC"}, {"," => "Comma (,)"}] + menu.confirm_keys :enter, {escape: "ESC"}, {"," => "Comma (,)"} +end # => # Choose your destiny? (Press ↑/↓ to move and Enter, ESC or Comma (,) to select) # ‣ Jax Sr @@ -1313,7 +1314,7 @@ You can also configure your keys using the DSL, similar to `:confirm_keys`: choices = ["gin", "gin tonic", "gin fizz", "beer"] prompt.multi_select("Select drinks?") do |menu| menu.choices choices - menu.select_keys [{ctr_s: "Ctrl-S"}, {space: "Spacebar"}] + menu.select_keys({ctr_s: "Ctrl-S"}, {space: "Spacebar"}) end # => diff --git a/examples/multi_select_custom_keys_dsl.rb b/examples/multi_select_custom_keys_dsl.rb index be9ec7c..760e3eb 100644 --- a/examples/multi_select_custom_keys_dsl.rb +++ b/examples/multi_select_custom_keys_dsl.rb @@ -7,6 +7,6 @@ drinks = %w[vodka beer wine whisky bourbon] prompt.multi_select("Choose your favourite drink?") do |menu| menu.choices drinks - menu.confirm_keys [:return, {escape: "Esc"}, "."] - menu.select_keys [{space: "Spacebar"}, :ctrl_s, ","] + menu.confirm_keys :return, {escape: "Esc"}, "." + menu.select_keys({space: "Spacebar"}, :ctrl_s, ",") end diff --git a/lib/tty/prompt/list.rb b/lib/tty/prompt/list.rb index 921c075..360fd79 100644 --- a/lib/tty/prompt/list.rb +++ b/lib/tty/prompt/list.rb @@ -50,7 +50,9 @@ def initialize(prompt, **options) @filterable = options.fetch(:filter) { false } @symbols = @prompt.symbols.merge(options.fetch(:symbols, {})) @quiet = options.fetch(:quiet) { @prompt.quiet } - @confirm_keys = init_action_keys(options.fetch(:confirm_keys) { self.class::DEFAULT_CONFIRM_KEYS }) + @confirm_keys = init_action_keys(options.fetch(:confirm_keys) do + self.class::DEFAULT_CONFIRM_KEYS + end) @filter = [] @filter_cache = {} @help = options[:help] @@ -85,17 +87,17 @@ def default(*default_values) # Set confirm keys # - # @param [Array String}>] value - # the new confirm_keys + # @param [Array String}>] keys + # the key(s) to confirm the selected item(s) # # @return [Hash{Symbol, String => String}] # # @api public - def confirm_keys(value = (not_set = true)) - return @confirm_keys if !@confirm_keys.nil? && not_set + def confirm_keys(*keys) + keys = keys.flatten + return @confirm_keys if keys.empty? - value = not_set ? self.class::DEFAULT_CONFIRM_KEYS : value - @confirm_keys = init_action_keys(value) + @confirm_keys = init_action_keys(keys) end # Initialize any default or custom action keys diff --git a/lib/tty/prompt/multi_list.rb b/lib/tty/prompt/multi_list.rb index 8cd165a..3999b2f 100644 --- a/lib/tty/prompt/multi_list.rb +++ b/lib/tty/prompt/multi_list.rb @@ -12,6 +12,8 @@ class Prompt class MultiList < List # The default keys that confirm the selected item(s) DEFAULT_CONFIRM_KEYS = %i[return enter].freeze + + # The default keys that select choices DEFAULT_SELECT_KEYS = %i[space].freeze # Create instance of TTY::Prompt::MultiList menu. @@ -55,7 +57,7 @@ def confirm super if valid end - def confirm_keys(value = (not_set = true)) + def confirm_keys(*keys) super check_conflicting_keys @confirm_keys @@ -63,17 +65,17 @@ def confirm_keys(value = (not_set = true)) # Set select keys # - # @param [Array String}>] value - # the new select_keys + # @param [Array String}>] keys + # the key(s) used for selecting choices # # @return [Hash{Symbol, String => String}] # # @api public - def select_keys(value = (not_set = true)) - return @select_keys if !@select_keys.nil? && not_set + def select_keys(*keys) + keys = keys.flatten + return @select_keys if keys.empty? - value = not_set ? self.class::DEFAULT_SELECT_KEYS : value - @select_keys = init_select_keys(value) + @select_keys = init_select_keys(keys) end # Callback fired when the selection key is pressed @@ -125,11 +127,15 @@ def keyctrl_r(*) private + # Initialize any default or custom select keys + # setting up their labels and dealing with any key conflicts + # + # @see List#init_action_keys + # + # @api private def init_select_keys(keys) @select_keys = init_action_keys(keys) - check_conflicting_keys - @select_keys end diff --git a/spec/unit/multi_select_spec.rb b/spec/unit/multi_select_spec.rb index e0b85e0..9cb2c52 100644 --- a/spec/unit/multi_select_spec.rb +++ b/spec/unit/multi_select_spec.rb @@ -247,8 +247,8 @@ def exit_message(prompt, choices) prompt.input.rewind value = prompt.multi_select("Select drinks?") do |menu| menu.choices choices - menu.confirm_keys [:ctrl_s] - menu.select_keys [:enter] + menu.confirm_keys :ctrl_s + menu.select_keys :enter end expect(value).to eq(["beer"]) diff --git a/spec/unit/select_spec.rb b/spec/unit/select_spec.rb index dc72ba6..9edc652 100644 --- a/spec/unit/select_spec.rb +++ b/spec/unit/select_spec.rb @@ -140,7 +140,7 @@ def exit_message(prompt, choice) prompt.input.rewind value = prompt.select("What size?") do |menu| menu.choices choices - menu.confirm_keys [:ctrl_s] + menu.confirm_keys :ctrl_s end expect(value).to eq("Large") expect(prompt.output.string).to eq([ From e8051553cd8c3ee0a32829552e11b47d5cf2e547 Mon Sep 17 00:00:00 2001 From: Eleni Lixourioti Date: Mon, 2 Aug 2021 15:25:55 +0200 Subject: [PATCH 24/33] Code review fixes - Improve performance of key_press - Remove accidental whitespace - Add missing documentation - Turn variable into constant --- lib/tty/prompt/list.rb | 18 ++++++++++++------ lib/tty/prompt/multi_list.rb | 3 +++ spec/unit/multi_select_spec.rb | 1 - 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/lib/tty/prompt/list.rb b/lib/tty/prompt/list.rb index 360fd79..7e7b559 100644 --- a/lib/tty/prompt/list.rb +++ b/lib/tty/prompt/list.rb @@ -22,6 +22,12 @@ class List # The default keys that confirm the selected item(s) DEFAULT_CONFIRM_KEYS = %i[space return enter].freeze + # The keys that signify "end of line" (EOL). + # Depending on whether we are on a Unix system / Windows + # the "Enter" key may translate to CR and/or LF characters. + # See also List#ensure_eol_compat + EOL_KEYS = %i[enter return].freeze + # Create instance of TTY::Prompt::List menu. # # @param Hash options @@ -50,7 +56,7 @@ def initialize(prompt, **options) @filterable = options.fetch(:filter) { false } @symbols = @prompt.symbols.merge(options.fetch(:symbols, {})) @quiet = options.fetch(:quiet) { @prompt.quiet } - @confirm_keys = init_action_keys(options.fetch(:confirm_keys) do + @confirm_keys = init_action_keys(options.fetch(:confirm_keys) do self.class::DEFAULT_CONFIRM_KEYS end) @filter = [] @@ -168,15 +174,14 @@ def key_help_label(key_name) # # @api private def ensure_eol_compat(keys) - eol_symbols = %i[enter return] key_symbols = keys.keys.sort_by(&:to_s) - key_intersection = eol_symbols & key_symbols + key_intersection = EOL_KEYS & key_symbols - if key_intersection.empty? || key_intersection == eol_symbols + if key_intersection.empty? || key_intersection == EOL_KEYS keys else eol_label = keys[key_intersection.first] - missing_key = (eol_symbols - key_intersection).first + missing_key = (EOL_KEYS - key_intersection).first keys.merge({missing_key => eol_label}) end end @@ -477,7 +482,8 @@ def confirm end def keypress(event) - if !(@confirm_keys.keys & [event.key.name, event.value]).empty? + if @confirm_keys.keys.include?(event.key.name) || + @confirm_keys.keys.include?(event.value) confirm elsif event.key.name == :tab keydown diff --git a/lib/tty/prompt/multi_list.rb b/lib/tty/prompt/multi_list.rb index 3999b2f..2f973cb 100644 --- a/lib/tty/prompt/multi_list.rb +++ b/lib/tty/prompt/multi_list.rb @@ -57,6 +57,9 @@ def confirm super if valid end + # @see List#confirm_keys + # + # @api public def confirm_keys(*keys) super check_conflicting_keys diff --git a/spec/unit/multi_select_spec.rb b/spec/unit/multi_select_spec.rb index 9cb2c52..96e5ea7 100644 --- a/spec/unit/multi_select_spec.rb +++ b/spec/unit/multi_select_spec.rb @@ -322,7 +322,6 @@ def exit_message(prompt, choices) ":confirm_keys [:space] are conflicting with the same keys in :select_keys") end - it "sets prompt prefix" do prompt = TTY::Prompt::Test.new(prefix: "[?] ") choices = %w[vodka beer wine whisky bourbon] From 9a70628308bfa53aa84f5ed8301ac549d3504ea1 Mon Sep 17 00:00:00 2001 From: Eleni Lixourioti Date: Mon, 2 Aug 2021 20:40:03 +0200 Subject: [PATCH 25/33] More code review fixes - Optimise keypress on multi_list - Appease some rubocop checks - Make multi_list DEFAULT_SELECT_KEYS overridable --- lib/tty/prompt/list.rb | 4 ++-- lib/tty/prompt/multi_list.rb | 11 +++++++---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/lib/tty/prompt/list.rb b/lib/tty/prompt/list.rb index 7e7b559..ea9167e 100644 --- a/lib/tty/prompt/list.rb +++ b/lib/tty/prompt/list.rb @@ -57,8 +57,8 @@ def initialize(prompt, **options) @symbols = @prompt.symbols.merge(options.fetch(:symbols, {})) @quiet = options.fetch(:quiet) { @prompt.quiet } @confirm_keys = init_action_keys(options.fetch(:confirm_keys) do - self.class::DEFAULT_CONFIRM_KEYS - end) + self.class::DEFAULT_CONFIRM_KEYS + end) @filter = [] @filter_cache = {} @help = options[:help] diff --git a/lib/tty/prompt/multi_list.rb b/lib/tty/prompt/multi_list.rb index 2f973cb..b178600 100644 --- a/lib/tty/prompt/multi_list.rb +++ b/lib/tty/prompt/multi_list.rb @@ -25,7 +25,9 @@ class MultiList < List def initialize(prompt, **options) super @selected = SelectedChoices.new - @select_keys = init_select_keys(options.fetch(:select_keys, DEFAULT_SELECT_KEYS)) + @select_keys = init_select_keys(options.fetch(:select_keys) do + self.class::DEFAULT_SELECT_KEYS + end) @help = options[:help] @echo = options.fetch(:echo, true) @min = options[:min] @@ -99,10 +101,11 @@ def select_choice # # @api private def keypress(event) - if (@select_keys.keys & [event.key.name, event.value]).empty? - super(event) - else + if @select_keys.keys.include?(event.key.name) || + @select_keys.keys.include?(event.value) select_choice + else + super(event) end end From b43a32a824d91707d80a457f535cad51b7d31686 Mon Sep 17 00:00:00 2001 From: Eleni Lixourioti Date: Fri, 20 Aug 2021 22:25:12 +0200 Subject: [PATCH 26/33] Make submit and select examples consistent, add missing yard option --- README.md | 12 +++++------- examples/select_confirm_key_labels.rb | 2 +- lib/tty/prompt/list.rb | 2 ++ lib/tty/prompt/multi_list.rb | 2 ++ 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 7e0b7d2..54e7fd8 100644 --- a/README.md +++ b/README.md @@ -967,7 +967,7 @@ You may also pass your own key labels to be displayed in the hint: ```ruby choices = ["Jax Sr", "Jax", "Jax Jr"] -prompt.select("Choose your destiny?", choices, confirm_keys: [:enter, {escape: "ESC"}, {"," => "Comma (,)"}]) +prompt.select("Choose your destiny?", choices, confirm_keys: [:enter, {ctrl_s: "Ctrl-S"}, {"," => "Comma (,)"}]) # => # Choose your destiny? (Press ↑/↓ to move and Enter, ESC or Comma (,) to select) # ‣ Jax Sr @@ -979,10 +979,9 @@ Confirm keys may be configured using the DSL, similar to `:choices`: ```ruby choices = ["Jax Sr", "Jax", "Jax Jr"] - prompt.select("Choose your destiny?") do |menu| menu.choices choices - menu.confirm_keys :enter, {escape: "ESC"}, {"," => "Comma (,)"} + menu.confirm_keys :enter, {ctrl_s: "Ctrl-S"}, {"," => "Comma (,)"} end # => # Choose your destiny? (Press ↑/↓ to move and Enter, ESC or Comma (,) to select) @@ -1251,7 +1250,7 @@ The user can then press the `Ctrl+S` or the `,` key to confirm their selection: # Select drinks? vodka, beer, whisky ``` -You may also configure the keys using the DSL, in the same way that is done for [select](#2628-confirm_keys) +You may also configure the keys using the DSL, in the same way that is done for [select](#2628-confirm_keys). #### 2.6.3.11 `:select_keys` @@ -1300,7 +1299,7 @@ Similar to the `:confirm_keys` option, you may also pass your own key labels to ```ruby choices = ["gin", "gin tonic", "gin fizz", "beer"] -prompt.multi_select("Select drinks?", choices, filter: true, select_keys: [{ctr_s: "Ctrl-S"}, {space: "Spacebar"}]) +prompt.multi_select("Select drinks?", choices, filter: true, select_keys: [{ctrl_s: "Ctrl-S"}, {space: "Spacebar"}]) # => # Select drinks? (Press ↑/↓ arrow keys to move, Ctrl-S or Spacebar/Ctrl+A|R to select (all|rev), Enter to finish and letters to filter) # ‣ ⬡ gin @@ -1314,9 +1313,8 @@ You can also configure your keys using the DSL, similar to `:confirm_keys`: choices = ["gin", "gin tonic", "gin fizz", "beer"] prompt.multi_select("Select drinks?") do |menu| menu.choices choices - menu.select_keys({ctr_s: "Ctrl-S"}, {space: "Spacebar"}) + menu.select_keys({ctrl_s: "Ctrl-S"}) end - # => # Select drinks? (Press ↑/↓ arrow keys to move, Ctrl-S or Spacebar/Ctrl+A|R to select (all|rev), and Enter to finish) # ‣ ⬡ gin diff --git a/examples/select_confirm_key_labels.rb b/examples/select_confirm_key_labels.rb index 9677133..05320f4 100644 --- a/examples/select_confirm_key_labels.rb +++ b/examples/select_confirm_key_labels.rb @@ -7,6 +7,6 @@ warriors = %w[Scorpion Kano Jax Kitana Raiden] answer = prompt.select("Choose your destiny?", warriors, - confirm_keys: [:enter, {escape: "ESC"}, ","]) + confirm_keys: [:enter, {ctrl_s: "Ctrl-S"}, ","]) puts answer.inspect \ No newline at end of file diff --git a/lib/tty/prompt/list.rb b/lib/tty/prompt/list.rb index ea9167e..8efd8e6 100644 --- a/lib/tty/prompt/list.rb +++ b/lib/tty/prompt/list.rb @@ -40,6 +40,8 @@ class List # the marker for the selected item # @option options [String] :enum # the delimiter for the item index + # @option options [Array String}>] + # :confirm_keys the key(s) to confirm the selected item(s) # # @api public def initialize(prompt, **options) diff --git a/lib/tty/prompt/multi_list.rb b/lib/tty/prompt/multi_list.rb index b178600..4af5531 100644 --- a/lib/tty/prompt/multi_list.rb +++ b/lib/tty/prompt/multi_list.rb @@ -20,6 +20,8 @@ class MultiList < List # # @param [Prompt] :prompt # @param [Hash] options + # @option options [Array String}>] + # :select_keys the key(s) used for selecting choices # # @api public def initialize(prompt, **options) From d42efff7c753091803252234c8e4f4ecc88c2180 Mon Sep 17 00:00:00 2001 From: Eleni Lixourioti Date: Fri, 20 Aug 2021 22:29:01 +0200 Subject: [PATCH 27/33] Change conflicting keys error message --- lib/tty/prompt/multi_list.rb | 5 +++-- spec/unit/multi_select_spec.rb | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/tty/prompt/multi_list.rb b/lib/tty/prompt/multi_list.rb index 4af5531..c82958d 100644 --- a/lib/tty/prompt/multi_list.rb +++ b/lib/tty/prompt/multi_list.rb @@ -155,8 +155,9 @@ def check_conflicting_keys return if conflicting_keys.empty? raise ConfigurationError, - ":confirm_keys #{conflicting_keys} are conflicting with " \ - "the same keys in :select_keys" + ":confirm_keys and :select_keys cannot use the same " \ + "#{conflicting_keys.map(&:inspect).join(', ')} " \ + "key#{'s' if conflicting_keys.size > 1}" end # Setup default options and active selection diff --git a/spec/unit/multi_select_spec.rb b/spec/unit/multi_select_spec.rb index 96e5ea7..15861c2 100644 --- a/spec/unit/multi_select_spec.rb +++ b/spec/unit/multi_select_spec.rb @@ -310,7 +310,7 @@ def exit_message(prompt, choices) expect { prompt.multi_select("Select drinks?", %w[vodka beer wine], confirm_keys: [:space]) }.to raise_error(TTY::Prompt::ConfigurationError, - ":confirm_keys [:space] are conflicting with the same keys in :select_keys") + ":confirm_keys and :select_keys cannot use the same :space key") end it "raises error when confirm and select keys clash (configured)" do @@ -319,7 +319,7 @@ def exit_message(prompt, choices) expect { prompt.multi_select("Select drinks?", %w[vodka beer wine], confirm_keys: %i[space ctrl_s], select_keys: [:space]) }.to raise_error(TTY::Prompt::ConfigurationError, - ":confirm_keys [:space] are conflicting with the same keys in :select_keys") + ":confirm_keys and :select_keys cannot use the same :space key") end it "sets prompt prefix" do From 9dea17b023c019af9e1ed75baffd7a31212b06e0 Mon Sep 17 00:00:00 2001 From: Eleni Lixourioti Date: Fri, 20 Aug 2021 22:36:17 +0200 Subject: [PATCH 28/33] Use helpers for confirm_keys DSL test --- spec/unit/select_spec.rb | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/spec/unit/select_spec.rb b/spec/unit/select_spec.rb index 9edc652..da089e5 100644 --- a/spec/unit/select_spec.rb +++ b/spec/unit/select_spec.rb @@ -144,13 +144,9 @@ def exit_message(prompt, choice) end expect(value).to eq("Large") expect(prompt.output.string).to eq([ - "\e[?25lWhat size? \e[90m(Press #{up_down} arrow to move and Ctrl+S to select)\e[0m\n", - "\e[32m#{symbols[:marker]} Large\e[0m\n", - " Medium\n", - " Small", - "\e[2K\e[1G\e[1A" * 3, - "\e[2K\e[1G", - "What size? \e[32mLarge\e[0m\n\e[?25h" + output_helper("What size?", choices, "Large", init: true, + hint: "Press #{up_down} arrow to move and Ctrl+S to select"), + exit_message("What size?", "Large") ].join) end From 7dc89e8318c4c8d95cc980baefd3d7685564e582 Mon Sep 17 00:00:00 2001 From: Eleni Lixourioti Date: Sat, 21 Aug 2021 16:12:12 +0200 Subject: [PATCH 29/33] Fix yard warnings in list.rb and multi_list.rb --- lib/tty/prompt/list.rb | 18 +++++++++--------- lib/tty/prompt/multi_list.rb | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/tty/prompt/list.rb b/lib/tty/prompt/list.rb index 8efd8e6..547c633 100644 --- a/lib/tty/prompt/list.rb +++ b/lib/tty/prompt/list.rb @@ -30,7 +30,7 @@ class List # Create instance of TTY::Prompt::List menu. # - # @param Hash options + # @param [Hash] options # the configuration options # @option options [Symbol] :default # the default active choice, defaults to 1 @@ -111,7 +111,7 @@ def confirm_keys(*keys) # Initialize any default or custom action keys # setting up their labels and dealing with compat # - # @param [Array String}>] + # @param [Array String}>] keys # # @return [Hash{Symbol, String => String}] # @@ -134,9 +134,9 @@ def init_action_keys(keys) # keys_with_labels(keys) # # => {enter: "Enter", ctrl_s: "Ctrl-S"} # - # @param [Array String}>] + # @param [Array String}>] keys # - # @return [String] + # @return [Hash{Symbol, String => String}] # # @api private def keys_with_labels(keys) @@ -148,7 +148,7 @@ def keys_with_labels(keys) # Convert a key name into a human-readable label # - # @param [Symbol, String] + # @param [Symbol, String] key_name # # @return [String] # @@ -170,7 +170,7 @@ def key_help_label(key_name) # ensure_eol_compat(keys) # # => {enter: "Enter", return: "Enter", ctrl_s: "Ctrl+S"} # - # @param [Hash{Symbol, String => String}] + # @param [Hash{Symbol, String => String}] keys # # @return [Hash{Symbol, String => String}] # @@ -284,7 +284,7 @@ def arrows_help # keys_help(keys) # # => "Enter" # - # @param [Hash{Symbol, String => String}] + # @param [Hash{Symbol, String => String}] keys # # @return [String] # @@ -368,8 +368,8 @@ def choices(values = (not_set = true)) # Call the list menu by passing question and choices # # @param [String] question + # @param [Array[Object]] possibilities # - # @param # @api public def call(question, possibilities, &block) choices(possibilities) @@ -625,7 +625,7 @@ def answer # Clear screen lines # - # @param [String] + # @param [Integer] lines # # @api private def refresh(lines) diff --git a/lib/tty/prompt/multi_list.rb b/lib/tty/prompt/multi_list.rb index c82958d..d9a90c7 100644 --- a/lib/tty/prompt/multi_list.rb +++ b/lib/tty/prompt/multi_list.rb @@ -18,7 +18,7 @@ class MultiList < List # Create instance of TTY::Prompt::MultiList menu. # - # @param [Prompt] :prompt + # @param [Prompt] prompt # @param [Hash] options # @option options [Array String}>] # :select_keys the key(s) used for selecting choices From 2334b89d5fa33b130d8d42d0ddcb8cac255af8e3 Mon Sep 17 00:00:00 2001 From: Eleni Lixourioti Date: Sat, 21 Aug 2021 16:26:27 +0200 Subject: [PATCH 30/33] Fix hint description in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 54e7fd8..92fb48c 100644 --- a/README.md +++ b/README.md @@ -161,7 +161,7 @@ Asking question with list of options couldn't be easier using `select` like so: ```ruby prompt.select("Choose your destiny?", %w(Scorpion Kano Jax)) # => -# Choose your destiny? (Press ↑/↓ arrow to move, Space or Enter to select) +# Choose your destiny? (Press ↑/↓ arrow to move and Space or Enter to select) # ‣ Scorpion # Kano # Jax From 44945c447e5dde8ec8754f077bd6f5fb59518dcf Mon Sep 17 00:00:00 2001 From: Eleni Lixourioti Date: Sun, 22 Aug 2021 11:56:26 +0200 Subject: [PATCH 31/33] Remove superfluous backticks from README code blocks --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 92fb48c..38c315e 100644 --- a/README.md +++ b/README.md @@ -1825,7 +1825,7 @@ You could also use `pastel`: ```ruby notice = Pastel.new.cyan.on_blue.detach prompt = TTY::Prompt.new(active_color: notice) -```` +``` Or use coloring of your own choice: @@ -1868,7 +1868,7 @@ You could also use `pastel`: ```ruby notice = Pastel.new.cyan.on_blue.detach prompt = TTY::Prompt.new(help_color: notice) -```` +``` Or use coloring of your own choice: @@ -1916,7 +1916,7 @@ Prompts such as `select`, `multi_select`, `expand`, `slider` support `:quiet` wh prompt = TTY::Prompt.new(quiet: true) # single prompt prompt.select("What is your favorite color?", %w(blue yellow orange)) -```` +``` ### 3.8 `:track_history` From 33b4beb6d07a54c932f7663bb755b79962ac2a09 Mon Sep 17 00:00:00 2001 From: Eleni Lixourioti Date: Sun, 22 Aug 2021 12:21:22 +0200 Subject: [PATCH 32/33] Add missing newlines before code blocks in README --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 38c315e..bbf8c08 100644 --- a/README.md +++ b/README.md @@ -407,6 +407,7 @@ end ``` Available letter casing settings are: + ```ruby :up # change to upper case :down # change to small case @@ -838,6 +839,7 @@ prompt.select("Choose your letter?", letters, per_page: 4) ``` You can also customise page navigation text using `:help` option: + ```ruby letters = ("A".."Z").to_a prompt.select("Choose your letter?") do |menu| @@ -956,6 +958,7 @@ After the user types "Jax": ``` After the user presses space: + ```ruby # => # Choose your destiny? (Filter: "Jax ") @@ -1245,6 +1248,7 @@ prompt.multi_select("Select drinks?", choices, confirm_keys: [:ctrl_s, ","]) ``` The user can then press the `Ctrl+S` or the `,` key to confirm their selection: + ```ruby # => # Select drinks? vodka, beer, whisky @@ -1288,6 +1292,7 @@ and then presses space: ``` The user can then press the `Ctrl+S` key combo to select the options: + ```ruby # => # Select drinks? (Filter: "gin ") @@ -1309,6 +1314,7 @@ prompt.multi_select("Select drinks?", choices, filter: true, select_keys: [{ctrl ``` You can also configure your keys using the DSL, similar to `:confirm_keys`: + ```ruby choices = ["gin", "gin tonic", "gin fizz", "beer"] prompt.multi_select("Select drinks?") do |menu| From 79cd017b350cbcc5185f1977e3c025bb9cca745e Mon Sep 17 00:00:00 2001 From: Eleni Lixourioti Date: Wed, 25 Aug 2021 19:41:56 +0200 Subject: [PATCH 33/33] Fix help text in README and add key @param descriptions --- README.md | 4 ++-- lib/tty/prompt/list.rb | 5 +++++ lib/tty/prompt/multi_list.rb | 2 ++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index bbf8c08..7236426 100644 --- a/README.md +++ b/README.md @@ -972,7 +972,7 @@ You may also pass your own key labels to be displayed in the hint: choices = ["Jax Sr", "Jax", "Jax Jr"] prompt.select("Choose your destiny?", choices, confirm_keys: [:enter, {ctrl_s: "Ctrl-S"}, {"," => "Comma (,)"}]) # => -# Choose your destiny? (Press ↑/↓ to move and Enter, ESC or Comma (,) to select) +# Choose your destiny? (Press ↑/↓ arrow to move and Enter, Ctrl-S or Comma (,) to select) # ‣ Jax Sr # Jax # Jax Jr @@ -987,7 +987,7 @@ prompt.select("Choose your destiny?") do |menu| menu.confirm_keys :enter, {ctrl_s: "Ctrl-S"}, {"," => "Comma (,)"} end # => -# Choose your destiny? (Press ↑/↓ to move and Enter, ESC or Comma (,) to select) +# Choose your destiny? (Press ↑/↓ arrow to move and Enter, Ctrl-S or Comma (,) to select) # ‣ Jax Sr # Jax # Jax Jr diff --git a/lib/tty/prompt/list.rb b/lib/tty/prompt/list.rb index 547c633..0d53bfd 100644 --- a/lib/tty/prompt/list.rb +++ b/lib/tty/prompt/list.rb @@ -112,6 +112,7 @@ def confirm_keys(*keys) # setting up their labels and dealing with compat # # @param [Array String}>] keys + # the key(s) as only name or name and label pair # # @return [Hash{Symbol, String => String}] # @@ -135,6 +136,7 @@ def init_action_keys(keys) # # => {enter: "Enter", ctrl_s: "Ctrl-S"} # # @param [Array String}>] keys + # the key(s) as only name or name and label pair # # @return [Hash{Symbol, String => String}] # @@ -149,6 +151,7 @@ def keys_with_labels(keys) # Convert a key name into a human-readable label # # @param [Symbol, String] key_name + # the key name to convert to label # # @return [String] # @@ -171,6 +174,7 @@ def key_help_label(key_name) # # => {enter: "Enter", return: "Enter", ctrl_s: "Ctrl+S"} # # @param [Hash{Symbol, String => String}] keys + # the key(s) as name and label pair # # @return [Hash{Symbol, String => String}] # @@ -285,6 +289,7 @@ def arrows_help # # => "Enter" # # @param [Hash{Symbol, String => String}] keys + # the key(s) as name and label pair # # @return [String] # diff --git a/lib/tty/prompt/multi_list.rb b/lib/tty/prompt/multi_list.rb index d9a90c7..d1abf84 100644 --- a/lib/tty/prompt/multi_list.rb +++ b/lib/tty/prompt/multi_list.rb @@ -149,6 +149,8 @@ def init_select_keys(keys) # Checks that there are no key options clashing # + # @raise [ConfigurationError] + # # @api private def check_conflicting_keys conflicting_keys = @confirm_keys.keys & @select_keys.keys