Module: AskTTY::Internal::Rendering
- Defined in:
- lib/asktty/internal/rendering.rb
Class Method Summary collapse
- .chop_grapheme(text) ⇒ Object
- .content_width(width) ⇒ Object
- .display_width(text) ⇒ Object
- .footer_lines(error_message:, help_line:, width:) ⇒ Object
- .frame(lines) ⇒ Object
- .graphemes(text) ⇒ Object
- .header_lines(title, details, width:) ⇒ Object
- .help_line(items, width:) ⇒ Object
- .placeholder_with_cursor(text) ⇒ Object
- .prompt_frame(title:, details:, help_items:, error_message:, width:, gap_before_body: false) ⇒ Object
- .split_lines(text) ⇒ Object
- .style_submitted_lines(lines, title_length:) ⇒ Object
- .submitted_frame(title, value, width:) ⇒ Object
- .take_segment(graphemes, width) ⇒ Object
- .wrap(text, width) ⇒ Object
- .wrap_exact(text, width) ⇒ Object
- .wrap_exact_line(line, width) ⇒ Object
- .wrap_line(line, width) ⇒ Object
Class Method Details
.chop_grapheme(text) ⇒ Object
10 11 12 |
# File 'lib/asktty/internal/rendering.rb', line 10 def chop_grapheme(text) graphemes(text.to_s)[0...-1].join end |
.content_width(width) ⇒ Object
105 106 107 |
# File 'lib/asktty/internal/rendering.rb', line 105 def content_width(width) [width.to_i - 2, 1].max end |
.display_width(text) ⇒ Object
14 15 16 |
# File 'lib/asktty/internal/rendering.rb', line 14 def display_width(text) Unicode::DisplayWidth.of(text.to_s.gsub(/\e\[[\d;]*m/, ""), ambiguous: 1) end |
.footer_lines(error_message:, help_line:, width:) ⇒ Object
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
# File 'lib/asktty/internal/rendering.rb', line 64 def (error_message:, help_line:, width:) lines = [] if && !.empty? lines.concat(wrap(, width).map do |line| ANSIStyle.error(line) end) end lines << help_line if help_line && !help_line.empty? return [] if lines.empty? [""] + lines end |
.frame(lines) ⇒ Object
109 110 111 112 113 114 |
# File 'lib/asktty/internal/rendering.rb', line 109 def frame(lines) content_lines = Array(lines).flat_map { |line| split_lines(line.to_s) } border = ANSIStyle.muted("┃") content_lines.map { |line| "#{border} #{line}" }.join("\n") end |
.graphemes(text) ⇒ Object
116 117 118 |
# File 'lib/asktty/internal/rendering.rb', line 116 def graphemes(text) text.scan(/\X/) end |
.header_lines(title, details, width:) ⇒ Object
56 57 58 59 60 61 62 |
# File 'lib/asktty/internal/rendering.rb', line 56 def header_lines(title, details, width:) lines = wrap(title.to_s, width).map { |line| ANSIStyle.title(line) } return lines unless details lines + wrap(details.to_s, width).map { |line| ANSIStyle.muted(line) } end |
.help_line(items, width:) ⇒ Object
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 |
# File 'lib/asktty/internal/rendering.rb', line 80 def help_line(items, width:) items = Array(items).map(&:to_s).reject(&:empty?) return "" if items.empty? line = +"" total_width = 0 items.each do |item| segment = total_width.zero? ? item : " • #{item}" segment_width = display_width(segment) if width.to_i.positive? && total_width + segment_width > width line << " …" if total_width + display_width(" …") <= width break end line << segment total_width += segment_width end return "" if line.empty? ANSIStyle.muted(line) end |
.placeholder_with_cursor(text) ⇒ Object
18 19 20 21 22 23 |
# File 'lib/asktty/internal/rendering.rb', line 18 def placeholder_with_cursor(text) first_grapheme, *rest = graphemes(text.to_s) return ANSIStyle.cursor unless first_grapheme ANSIStyle.style(first_grapheme, foreground: 8, background: 2) + ANSIStyle.muted(rest.join) end |
.prompt_frame(title:, details:, help_items:, error_message:, width:, gap_before_body: false) ⇒ Object
25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
# File 'lib/asktty/internal/rendering.rb', line 25 def prompt_frame(title:, details:, help_items:, error_message:, width:, gap_before_body: false) content_width = self.content_width(width) lines = header_lines(title, details, width: content_width) lines << "" if gap_before_body && !lines.empty? lines.concat(Array(yield(content_width))) lines.concat( ( error_message: , help_line: help_line(help_items, width: content_width), width: content_width ) ) frame(lines) end |
.split_lines(text) ⇒ Object
120 121 122 123 124 |
# File 'lib/asktty/internal/rendering.rb', line 120 def split_lines(text) return [""] if text.empty? text.split("\n", -1) end |
.style_submitted_lines(lines, title_length:) ⇒ Object
126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 |
# File 'lib/asktty/internal/rendering.rb', line 126 def style_submitted_lines(lines, title_length:) remaining_title_length = title_length lines.map do |line| line_graphemes = graphemes(line) if remaining_title_length >= line_graphemes.length remaining_title_length -= line_graphemes.length ANSIStyle.title(line) elsif remaining_title_length.positive? title_text = line_graphemes[0, remaining_title_length].join value_text = line_graphemes[remaining_title_length..].to_a.join remaining_title_length = 0 ANSIStyle.title(title_text) + ANSIStyle.text(value_text) else ANSIStyle.text(line) end end end |
.submitted_frame(title, value, width:) ⇒ Object
40 41 42 43 44 45 46 |
# File 'lib/asktty/internal/rendering.rb', line 40 def submitted_frame(title, value, width:) prefix = "#{title}:" summary = value.to_s.empty? ? prefix : "#{prefix} #{value}" lines = wrap(summary, content_width(width)) frame(style_submitted_lines(lines, title_length: graphemes(prefix).length)) end |
.take_segment(graphemes, width) ⇒ Object
189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 |
# File 'lib/asktty/internal/rendering.rb', line 189 def take_segment(graphemes, width) segment = [] segment_width = 0 last_whitespace_index = nil graphemes.each_with_index do |grapheme, index| grapheme_width = display_width(grapheme) if segment_width.zero? && grapheme_width > width return [[grapheme], grapheme.match?(/\s/) ? 0 : nil] end break if segment_width.positive? && segment_width + grapheme_width > width segment << grapheme segment_width += grapheme_width last_whitespace_index = index if grapheme.match?(/\s/) break if segment_width >= width end [segment, last_whitespace_index] end |
.wrap(text, width) ⇒ Object
48 49 50 |
# File 'lib/asktty/internal/rendering.rb', line 48 def wrap(text, width) split_lines(text.to_s).flat_map { |line| wrap_line(line, width) } end |
.wrap_exact(text, width) ⇒ Object
52 53 54 |
# File 'lib/asktty/internal/rendering.rb', line 52 def wrap_exact(text, width) split_lines(text.to_s).flat_map { |line| wrap_exact_line(line, width) } end |
.wrap_exact_line(line, width) ⇒ Object
174 175 176 177 178 179 180 181 182 183 184 185 186 187 |
# File 'lib/asktty/internal/rendering.rb', line 174 def wrap_exact_line(line, width) return [""] if line.empty? lines = [] remaining = graphemes(line) until remaining.empty? segment, = take_segment(remaining, width) lines << segment.join remaining = remaining[segment.length..] || [] end lines end |
.wrap_line(line, width) ⇒ Object
147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 |
# File 'lib/asktty/internal/rendering.rb', line 147 def wrap_line(line, width) return [""] if line.empty? lines = [] remaining = graphemes(line) until remaining.empty? segment, last_whitespace_index = take_segment(remaining, width) if segment.length == remaining.length lines << segment.join break end if last_whitespace_index&.positive? lines << segment[0...last_whitespace_index].join.rstrip remaining = remaining[(last_whitespace_index + 1)..] || [] remaining = remaining.drop_while { |grapheme| grapheme.match?(/\s/) } else lines << segment.join remaining = remaining[segment.length..] || [] end end lines end |