Module: Clack::Utils
- Defined in:
- lib/clack/utils.rb
Overview
Utility functions for text manipulation and formatting
Class Method Summary collapse
-
.display_width(string) ⇒ Integer
Calculate the terminal display width (columns) of a string.
-
.rendered_rows(text, width) ⇒ Integer
Count the terminal rows
textoccupies once printed: one per line, plus one more for everywidthcolumns a line overflows, since the terminal soft-wraps lines wider than the pane. -
.strip_ansi(text) ⇒ String
Strip ANSI escape sequences from text.
-
.truncate(text, width, ellipsis: "...") ⇒ String
Truncate text to width with ellipsis.
-
.visible_length(text) ⇒ Integer
Get visible length (display width in columns) of text after stripping ANSI.
-
.wrap(text, width) ⇒ String
Wrap text to a specified width, preserving ANSI codes.
-
.wrap_with_prefix(text, prefix, width) ⇒ String
Wrap text with a prefix on each line.
Class Method Details
.display_width(string) ⇒ Integer
Calculate the terminal display width (columns) of a string. ASCII and most chars: width 1. CJK ideographs, fullwidth forms, common emoji: width 2. Zero-width joiners, combining marks, variation selectors: width 0.
44 45 46 47 48 49 50 51 52 53 |
# File 'lib/clack/utils.rb', line 44 def display_width(string) str = string.to_s return 0 if str.empty? width = 0 str.grapheme_clusters.each do |cluster| width += grapheme_width(cluster) end width end |
.rendered_rows(text, width) ⇒ Integer
Count the terminal rows text occupies once printed: one per line, plus
one more for every width columns a line overflows, since the terminal
soft-wraps lines wider than the pane. A line exactly width wide stays
on one row (the terminal defers the wrap until the next character).
Trailing text without a newline only counts the rows it wrapped onto;
the cursor is still on its last row.
31 32 33 34 35 36 37 |
# File 'lib/clack/utils.rb', line 31 def rendered_rows(text, width) text.to_s.each_line.sum do |line| columns = visible_length(line.chomp) rows = columns.zero? ? 1 : (columns + width - 1) / width line.end_with?("\n") ? rows : rows - 1 end end |
.strip_ansi(text) ⇒ String
Strip ANSI escape sequences from text
10 11 12 |
# File 'lib/clack/utils.rb', line 10 def strip_ansi(text) text.to_s.gsub(/\e\[[0-9;]*[a-zA-Z]/, "") end |
.truncate(text, width, ellipsis: "...") ⇒ String
Truncate text to width with ellipsis
88 89 90 91 92 93 94 95 96 |
# File 'lib/clack/utils.rb', line 88 def truncate(text, width, ellipsis: "...") return text if visible_length(text) <= width target = width - visible_length(ellipsis) return ellipsis if target <= 0 # Handle ANSI codes: we need to truncate visible chars while preserving codes truncate_visible(text, target) + ellipsis end |
.visible_length(text) ⇒ Integer
Get visible length (display width in columns) of text after stripping ANSI. Uses display_width to correctly measure CJK, emoji, combining chars.
18 19 20 |
# File 'lib/clack/utils.rb', line 18 def visible_length(text) display_width(strip_ansi(text)) end |
.wrap(text, width) ⇒ String
Wrap text to a specified width, preserving ANSI codes
59 60 61 62 63 64 65 66 67 |
# File 'lib/clack/utils.rb', line 59 def wrap(text, width) return text if width <= 0 lines = [] text.to_s.each_line do |line| lines.concat(wrap_line(line.chomp, width)) end lines.join("\n") end |
.wrap_with_prefix(text, prefix, width) ⇒ String
Wrap text with a prefix on each line
74 75 76 77 78 79 80 81 |
# File 'lib/clack/utils.rb', line 74 def wrap_with_prefix(text, prefix, width) prefix_len = visible_length(prefix) content_width = width - prefix_len return text if content_width <= 0 wrapped = wrap(text, content_width) wrapped.lines.map { |line| "#{prefix}#{line.chomp}" }.join("\n") end |