File: cli_inputs.rb

package info (click to toggle)
gitlab 17.6.5-19
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 629,368 kB
  • sloc: ruby: 1,915,304; javascript: 557,307; sql: 60,639; xml: 6,509; sh: 4,567; makefile: 1,239; python: 406
file content (119 lines) | stat: -rwxr-xr-x 4,204 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# frozen_string_literal: true

# Helpers related to configuration of TTY::Prompt prompts
module InternalEventsCli
  module Helpers
    module CliInputs
      def prompt_for_array_selection(message, choices, default = nil, **opts, &formatter)
        formatter ||= ->(choice) { choice.sort.join(", ") }

        choices = choices.map do |choice|
          { name: formatter.call(choice), value: choice }
        end

        cli.select(message, choices, **select_opts, **opts) do |menu|
          menu.enum "."
          menu.default formatter.call(default) if default
        end
      end

      # Prompts the user to input text. Prefer this over calling cli#ask directly (so styling is consistent).
      #
      #
      # @return [String, nil] user-provided text
      # @param message [String] a single line prompt/question or last line of a prompt
      # @param value [String, nil] prepopulated as the answer which user can accept/modify
      # @option multiline [Boolean] indicates that any help text or prompt prefix will be printed on another line
      #                             before calling #prompt_for_text -->  ex) see MetricDefiner#prompt_for_description
      # @yield [TTY::Prompt::Question]
      # @see https://github.com/piotrmurach/tty-prompt?tab=readme-ov-file#21-ask
      def prompt_for_text(message, value = nil, multiline: false, **opts)
        prompt = message.dup # mutable for concat in #ask callback

        options = { **input_opts, **opts }
        value ||= options.delete(:value)
        options.delete(:prefix) if multiline

        cli.ask(prompt, **options) do |q|
          q.value(value) if value

          yield q if block_given?

          if multiline
            # wrap error messages so they render nicely with prompt
            q.messages.each do |key, error|
              closing_text = "\n#{format_error('<<|')}" if error.lines.length > 1

              q.messages[key] = [error, closing_text, "\n\n\n"].join('')
            end
          else
            # append help text only if this line includes the formatted 'prompt' prefix,
            # otherwise depend on the caller to print the help text if needed
            prompt.concat(" #{q.required ? input_required_text : input_optional_text(value)}")
          end
        end
      end

      def input_opts
        { prefix: format_prompt('Input text: ') }
      end

      def yes_no_opts
        { prefix: format_prompt('Yes/No: ') }
      end

      # Provide to cli#select as kwargs for consistent style/ux
      def select_opts
        {
          prefix: format_prompt('Select one: '),
          cycle: true,
          show_help: :always,
          # Strip colors so #format_selection is applied uniformly
          active_color: ->(choice) { format_selection(clear_format(choice)) }
        }
      end

      # Provide to cli#multiselect as kwargs for consistent style/ux
      def multiselect_opts
        { **select_opts, prefix: format_prompt('Select multiple: '), min: 1 }
      end

      # Accepts a number of lines occupied by text, so remaining
      # screen real estate can be filled with select options
      def filter_opts(header_size: nil)
        {
          filter: true,
          per_page: header_size ? [(window_height - header_size), 10].max : 30
        }
      end

      # For use when menu options are disabled by being grayed out
      def disabled_format_callback
        proc { |menu| menu.symbols(cross: format_help("✘")) }
      end

      # Help text to use with required, multiline cli#ask prompts.
      # Otherwise, prefer #prompt_for_text.
      def input_required_text
        format_help("(leave blank for help)")
      end

      # Help text to use with optional, multiline cli#ask prompts.
      # Otherwise, prefer #prompt_for_text.
      def input_optional_text(value)
        format_help("(enter to #{value ? 'submit' : 'skip'})")
      end

      def disableable_option(value:, disabled:, name: nil)
        should_disable = yield
        name ||= value

        {
          value: value,
          name: (should_disable ? format_help(name) : name),
          disabled: (disabled if should_disable)
        }
      end
    end
  end
end