Class: Terminal::Text::Formatter

Inherits:
Object
  • Object
show all
Defined in:
lib/terminal/text/formatter.rb

Overview

Processes text with BBCode markup and ANSI escape codes into display-width-aware lines. Supports Unicode-aware word-wrapping, alignment, padding, and prefix/suffix decorations.

Examples:

Basic word-wrapping

fmt = Terminal::Text::Formatter.new('Hello World, this is a test')
fmt.lines(width: 12)
# => ["Hello World,", "this is a", "test"]

Formatted output with alignment

Terminal::Text::Formatter.format(
  'Hello', align: :center, width: 20
)
# => ["       Hello        "]

See Also:

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(*str, ansi: true, bbcode: true, spaces: true, eol: true) ⇒ Formatter

Create a new formatter with given text and options.

Parameters:

  • str (Array<#to_s>)

    text to process

  • ansi (true, false) (defaults to: true)

    recognize ANSI escape codes

  • bbcode (true, false) (defaults to: true)

    process BBCode markup

  • spaces (true, false) (defaults to: true)

    preserve whitespace; when false leading/trailing spaces and multiple spaces are collapsed

  • eol (true, false) (defaults to: true)

    preserve line endings; when false newlines are treated as spaces



93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/terminal/text/formatter.rb', line 93

def initialize(*str, ansi: true, bbcode: true, spaces: true, eol: true)
  @lex = []
  return if str.empty?
  ansi ? _generate_ansi(str, bbcode) : _generate(str, bbcode)
  return if @lex.empty?
  @keep_paragraph = true
  @lex = _ignore_whitespace unless spaces
  case eol
  when false, nil
    @lex = _ignore_newline
  when :no_paragraph
    @keep_paragraph = false
  end
end

Class Method Details

.[](*str, with_size: false, ansi: true, bbcode: true, spaces: true, eol: true, width: nil) ⇒ Array<String, Integer>

Parse text into lines, optionally with display widths.

Examples:

Terminal::Text::Formatter['Hello World', width: 5]
# => ["Hello", "World"]

With size information

Terminal::Text::Formatter['Hello', with_size: true]
# => [["Hello", 5]]

Parameters:

  • with_size (true, false) (defaults to: false)

    when true, return [line, width] pairs instead of plain strings

  • str (Array<#to_s>)

    text to process

  • ansi (true, false) (defaults to: true)

    recognize ANSI escape codes

  • bbcode (true, false) (defaults to: true)

    process BBCode markup

  • spaces (true, false) (defaults to: true)

    preserve whitespace; when false leading/trailing spaces and multiple spaces are collapsed

  • eol (true, false) (defaults to: true)

    preserve line endings; when false newlines are treated as spaces

  • width (#to_i, nil) (defaults to: nil)

    maximum line width in columns; nil returns unwrapped lines

Returns:

  • (Array<String, Integer>)

    pairs of [line_text, display_width]

See Also:



39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/terminal/text/formatter.rb', line 39

def self.[](
  *str,
  with_size: false,
  ansi: true,
  bbcode: true,
  spaces: true,
  eol: true,
  width: nil
)
  ret = new(*str, ansi:, bbcode:, spaces:, eol:).lines_with_size(width:)
  with_size ? ret : ret.map(&:first)
end

.format(*str, ansi: true, bbcode: true, spaces: true, eol: true, align: nil, width: nil, height: nil, padding: nil, prefix: nil, suffix: nil) ⇒ Object

Format text with alignment, padding, and decorations.

Parameters:

  • str (Array<#to_s>)

    text to process

  • ansi (true, false) (defaults to: true)

    recognize ANSI escape codes

  • bbcode (true, false) (defaults to: true)

    process BBCode markup

  • spaces (true, false) (defaults to: true)

    preserve whitespace; when false leading/trailing spaces and multiple spaces are collapsed

  • eol (true, false) (defaults to: true)

    preserve line endings; when false newlines are treated as spaces

  • align (Symbol, nil) (defaults to: nil)

    text alignment: :left, :right, :center, or nil (no fill)

  • width (#to_i, nil) (defaults to: nil)

    output line width in columns

  • height (#to_i, nil) (defaults to: nil)

    number of output lines; negative values take lines from the end

  • padding (#to_i, Array, nil) (defaults to: nil)

    CSS-style padding

    • 1 value: all sides;
    • 2 values: [vertical, horizontal];
    • 3 values: [top, horizontal, bottom];
    • 4 values: [top, right, bottom, left]
  • prefix (#to_s, nil) (defaults to: nil)

    string prepended to each line

  • suffix (#to_s, nil) (defaults to: nil)

    string appended to each line

Raises:

  • (ArgumentError)

    if width is zero or negative

See Also:



60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/terminal/text/formatter.rb', line 60

def self.format(
  *str,
  ansi: true,
  bbcode: true,
  spaces: true,
  eol: true,
  align: nil,
  width: nil,
  height: nil,
  padding: nil,
  prefix: nil,
  suffix: nil
)
  new(*str, ansi:, bbcode:, spaces:, eol:).format(
    align:,
    width:,
    height:,
    padding:,
    prefix:,
    suffix:
  )
end

Instance Method Details

#empty?true, false

Whether the formatter has any content.

Returns:

  • (true, false)


227
# File 'lib/terminal/text/formatter.rb', line 227

def empty? = @lex.empty?

#format(align: nil, width: nil, height: nil, padding: nil, prefix: nil, suffix: nil) ⇒ Array<String>

Format lines with alignment, padding, and decorations.

Examples:

Centered text with padding

fmt = Terminal::Text::Formatter.new('Hi')
fmt.format(align: :center, width: 10, padding: [0, 1])
# => [" Hi         "]

Parameters:

  • align (Symbol, nil) (defaults to: nil)

    text alignment: :left, :right, :center, or nil (no fill)

  • width (#to_i, nil) (defaults to: nil)

    output line width in columns

  • height (#to_i, nil) (defaults to: nil)

    number of output lines; negative values take lines from the end

  • padding (#to_i, Array, nil) (defaults to: nil)

    CSS-style padding

    • 1 value: all sides;
    • 2 values: [vertical, horizontal];
    • 3 values: [top, horizontal, bottom];
    • 4 values: [top, right, bottom, left]
  • prefix (#to_s, nil) (defaults to: nil)

    string prepended to each line

  • suffix (#to_s, nil) (defaults to: nil)

    string appended to each line

Returns:

  • (Array<String>)

    formatted output lines

Raises:

  • (ArgumentError)

    if width is zero or negative



160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
# File 'lib/terminal/text/formatter.rb', line 160

def format(
  align: nil,
  width: nil,
  height: nil,
  padding: nil,
  prefix: nil,
  suffix: nil
)
  top, right, bottom, left = _padding(padding)

  if height
    return [] if (height = height.to_i) == 0
    if height < 0
      tail = true
      height = -height
    end
    height -= top
    height -= -bottom
    return [] if height < 1
  end

  space = SPACE_CACHE.dup

  if width
    w = (width = width.to_i) - left - right
    raise(ArgumentError, "invalid width - #{width.inspect}") if w < 1
    lws = _limited(w)
    lws = tail ? lws.last(height) : lws.take(height) if height
    empty = "#{prefix}#{space[width]}#{suffix}" if top + bottom > 0
  else
    lws = @unlimited || _unlimited
    lws = tail ? lws.last(height) : lws.take(height) if height
    w = lws.empty? ? 0 : lws.max_by(&:last)[-1]
    if top + bottom > 0
      empty = "#{prefix}#{space[w + left + right]}#{suffix}"
    end
  end

  ret = []
  ret.fill(empty, 0, top) if top > 0
  left = left < 1 ? prefix.to_s : "#{prefix}#{space[left]}"
  right = right < 1 ? suffix.to_s : "#{space[right]}#{suffix}"

  # TODO: styling

  case align
  when :left
    lws.each { |l, s| ret << "#{left}#{l}#{space[w - s]}#{right}" }
  when :right
    lws.each { |l, s| ret << "#{left}#{space[w - s]}#{l}#{right}" }
  when :center
    lws.each do |l, s|
      rs = ((s = w - s) / 2.0).round
      next ret << "#{left}#{l}#{right}" if rs < 1
      ret << "#{left}#{space[s - rs]}#{l}#{space[rs]}#{right}"
    end
  else
    lws.each { |l, _| ret << "#{left}#{l}#{right}" }
  end

  return ret.fill(empty, ret.size, bottom) if bottom > 0
  ret.empty? ? ret << '' : ret
end

#lines(width: nil) ⇒ Array<String>

Word-wrap and return lines as strings.

Examples:

fmt = Terminal::Text::Formatter.new('Hello World')
fmt.lines(width: 5)
# => ["Hello", "World"]

Parameters:

  • width (#to_i, nil) (defaults to: nil)

    maximum line width in columns

Returns:

  • (Array<String>)

Raises:

  • (ArgumentError)

    if width is zero or negative



137
# File 'lib/terminal/text/formatter.rb', line 137

def lines(width: nil) = lines_with_size(width:).map(&:first)

#lines_with_size(width: nil) ⇒ Array<String, Integer>

Word-wrap and return lines with their display widths.

Examples:

fmt = Terminal::Text::Formatter.new('Hello World')
fmt.lines_with_size(width: 5)
# => [["Hello", 5], ["World", 5]]

Parameters:

  • width (#to_i, nil) (defaults to: nil)

    maximum line width in columns; nil returns unwrapped lines

Returns:

  • (Array<String, Integer>)

    pairs of [line_text, display_width]

Raises:

  • (ArgumentError)
  • (ArgumentError)

    if width is zero or negative



120
121
122
123
124
125
# File 'lib/terminal/text/formatter.rb', line 120

def lines_with_size(width: nil)
  return [] if @lex.empty?
  return @unlimited || _unlimited unless width
  (w = width.to_i) > 0 and return _limited(w)
  raise(ArgumentError, "invalid width - #{width.inspect}")
end

#max_line_widthInteger

Maximum display width of any line.

Examples:

fmt = Terminal::Text::Formatter.new("short\na longer line")
fmt.max_line_width # => 13

Returns:

  • (Integer)

    widest line in columns, 0 when empty



236
237
238
239
# File 'lib/terminal/text/formatter.rb', line 236

def max_line_width
  @max_line_width ||=
    (@lex.empty? ? 0 : (@unlimited || _unlimited).max_by(&:last)[-1])
end