File: formatting.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 (160 lines) | stat: -rwxr-xr-x 5,233 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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# frozen_string_literal: true

# Helpers related to visual formatting of outputs
module InternalEventsCli
  module Helpers
    module Formatting
      DEFAULT_WINDOW_WIDTH = 100
      DEFAULT_WINDOW_HEIGHT = 30

      # When to format as "info":
      # - When a header is needed to organize contextual
      #   information. These headers should always be all caps.
      # - As a supplemental way to highlight the most important
      #   text within a menu or informational text.
      # - Optionally, for URLs
      def format_info(string)
        pastel.cyan(string)
      end

      # When to format as "warning":
      # - To highlight the first sentence/phrase describing a
      #   problem the user needs to address. Any further text
      #   explantion should be left unformatted.
      # - To highlight an explanation of why the user cannot take
      #   a particular action.
      def format_warning(string)
        pastel.yellow(string)
      end

      # When to format as "selection":
      # - As a supplemental way of indicating something was
      #   selected or the current state of an interaction.
      def format_selection(string)
        pastel.green(string)
      end

      # When to format as "help":
      # - To format supplemental information on how to interact
      #   with prompts. This should always be in parenthesis.
      # - To indicate disabled or unavailable menu options.
      # - To indicate meta-information in menu options or
      #   informational text.
      def format_help(string)
        pastel.bright_black(string)
      end

      # When to format as "prompt":
      # - When we need the user to input information. The text
      #   should describe the action the user should take to move
      #   forward, like `Input text` or `Select one`
      # - As header text on multi-screen steps in a flow. Always
      #   include a counter when this is the case.
      def format_prompt(string)
        pastel.magenta(string)
      end

      # When to format as "error":
      # - When the CLI encounters unexpected problems that may
      #   require broader changes by the Analytics Instrumentation
      #   Group or out of band configuration.
      # - To highlight special characters used to symbolize that
      #   there was an error or that an option is not available.
      def format_error(string)
        pastel.red(string)
      end

      # Strips all existing color/text style
      def clear_format(string)
        pastel.strip(string)
      end

      # When to format as "heading":
      # - At the beginning or end of complete flows, to create
      #   visual separation and indicate logical breakpoints.
      def format_heading(string)
        [divider, pastel.cyan(string), divider].join("\n")
      end

      # Used for grouping prompts that occur on the same screen
      # or as part of the same step of a flow.
      #
      # Counter is exluded if total is 1.
      # The subject's formatting is extended to the counter.
      #
      # @return [String] ex) -- EATING COOKIES (2/3): Chocolate Chip --
      # @param subject [String] describes task generically ex) EATING COOKIES
      # @param item [String] describes specific context ex) Chocolate Chip
      # @param count [Integer] ex) 2
      # @param total [Integer] ex) 3
      def format_subheader(subject, item, count, total)
        formatting_end = "\e[0m"
        suffix = formatting_end if subject[-formatting_end.length..] == formatting_end

        "-- #{[subject.chomp(formatting_end), counter(count, total)].compact.join(' ')}:#{suffix} #{item} --"
      end

      def format_prefix(prefix, string)
        string.lines.map { |line| line.prepend(prefix) }.join
      end

      # When to use a divider:
      # - As separation between whole flows or format the layout
      #   of a screen or the layout of CLI outputs.
      # - Dividers should not be used to differentiate between
      #   prompts on the same screen.
      def divider
        "-" * window_size
      end

      def progress_bar(step, total, titles = [])
        breadcrumbs = [
          titles[0..(step - 1)],
          format_selection(titles[step]),
          titles[(step + 1)..]
        ]

        status = " Step #{step} / #{total} : #{breadcrumbs.flatten.join(' > ')}"
        total_length = window_size - 4
        step_length = step / total.to_f * total_length

        incomplete = '-' * [(total_length - step_length - 1), 0].max
        complete = '=' * [(step_length - 1), 0].max
        "#{status}\n|==#{complete}>#{incomplete}|\n"
      end

      # Formats a counter if there's anything to count
      #
      # @return [String, nil] ex) "(3/4)""
      def counter(idx, total)
        "(#{idx + 1}/#{total})" if total > 1
      end

      private

      def pastel
        @pastel ||= Pastel.new
      end

      def window_size
        Integer(fetch_window_size)
      rescue StandardError
        DEFAULT_WINDOW_WIDTH
      end

      def window_height
        Integer(fetch_window_height)
      rescue StandardError
        DEFAULT_WINDOW_HEIGHT
      end

      def fetch_window_size
        `tput cols`
      end

      def fetch_window_height
        `tput lines`
      end
    end
  end
end