- 
                Notifications
    
You must be signed in to change notification settings  - Fork 57
 
Fix interactive prompt for options > terminal rows #35
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | 
|---|---|---|
| 
          
            
          
           | 
    @@ -42,7 +42,7 @@ def call | |
| CLI::UI.raw { print(ANSI.hide_cursor) } | ||
| while @answer.nil? | ||
| render_options | ||
| wait_for_user_input | ||
| wait_for_actionable_user_input | ||
| reset_position | ||
| end | ||
| clear_output | ||
| 
          
            
          
           | 
    @@ -74,14 +74,15 @@ def clear_output | |
| end | ||
| 
     | 
||
| def num_lines | ||
| options = presented_options.map(&:first) | ||
| # @options will be an array of questions but each option can be multi-line | ||
| # so to get the # of lines, you need to join then split | ||
| 
     | 
||
| # empty_option_count is needed since empty option titles are omitted | ||
| # from the line count when reject(&:empty?) is called | ||
| 
     | 
||
| empty_option_count = @options.count(&:empty?) | ||
| joined_options = @options.join("\n") | ||
| empty_option_count = options.count(&:empty?) | ||
| joined_options = options.join("\n") | ||
| joined_options.split("\n").reject(&:empty?).size + empty_option_count | ||
| end | ||
| 
     | 
||
| 
        
          
        
         | 
    @@ -107,6 +108,13 @@ def select_bool(char) | |
| @answer = @options.index(opt) + 1 | ||
| end | ||
| 
     | 
||
| def wait_for_actionable_user_input | ||
| last_active = @active | ||
| while @active == last_active && @answer.nil? | ||
| wait_for_user_input | ||
| end | ||
| end | ||
| 
     | 
||
| # rubocop:disable Style/WhenThen,Layout/SpaceBeforeSemicolon | ||
| def wait_for_user_input | ||
| char = read_char | ||
| 
          
            
          
           | 
    @@ -157,12 +165,55 @@ def raw_tty! | |
| end | ||
| end | ||
| 
     | 
||
| def presented_options(recalculate: false) | ||
| return @presented_options unless recalculate | ||
| 
     | 
||
| @presented_options = @options.zip(1..Float::INFINITY) | ||
| 
         There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 
 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This change would need   | 
||
| while num_lines > max_options | ||
| # try to keep the selection centered in the window: | ||
| if distance_from_selection_to_end > distance_from_start_to_selection | ||
| # selection is closer to top than bottom, so trim a row from the bottom | ||
| ensure_last_item_is_continuation_marker | ||
| @presented_options.delete_at(-2) | ||
| else | ||
| # selection is closer to bottom than top, so trim a row from the top | ||
| ensure_first_item_is_continuation_marker | ||
| @presented_options.delete_at(1) | ||
| end | ||
| end | ||
| 
     | 
||
| @presented_options | ||
| end | ||
| 
     | 
||
| def distance_from_selection_to_end | ||
| last_visible_option_number = @presented_options[-1].last || @presented_options[-2].last | ||
| last_visible_option_number - @active | ||
| end | ||
| 
     | 
||
| def distance_from_start_to_selection | ||
| first_visible_option_number = @presented_options[0].last || @presented_options[1].last | ||
| @active - first_visible_option_number | ||
| end | ||
| 
     | 
||
| def ensure_last_item_is_continuation_marker | ||
| @presented_options.push(["...", nil]) if @presented_options.last.last | ||
| end | ||
| 
     | 
||
| def ensure_first_item_is_continuation_marker | ||
| @presented_options.unshift(["...", nil]) if @presented_options.first.last | ||
| end | ||
| 
     | 
||
| 
     | 
||
| def max_options | ||
| @max_options ||= CLI::UI::Terminal.height - 2 # Keeps a one line question visible | ||
| end | ||
| 
     | 
||
| def render_options | ||
| max_num_length = (@options.size + 1).to_s.length | ||
| @options.each_with_index do |choice, index| | ||
| num = index + 1 | ||
| 
     | 
||
| presented_options(recalculate: true).each do |choice, num| | ||
| padding = ' ' * (max_num_length - num.to_s.length) | ||
| message = " #{num}.#{padding}" | ||
| message = " #{num}#{num ? '.' : ' '}#{padding}" | ||
| message += choice.split("\n").map { |l| " {{bold:#{l}}}" }.join("\n") | ||
| 
     | 
||
| if num == @active | ||
| 
        
          
        
         | 
    @@ -172,7 +223,7 @@ def render_options | |
| end | ||
| 
     | 
||
| CLI::UI.with_frame_color(:blue) do | ||
| puts CLI::UI.fmt(message) | ||
| puts CLI::UI.fmt(message) + CLI::UI::ANSI.clear_to_end_of_line | ||
| end | ||
| end | ||
| end | ||
| 
          
            
          
           | 
    ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is really hard to follow.
I feel like it would do well to have some comments throughout explaining the code as it's quite logic dense and sort of magic with the
[-2].lastetc