Module: Kramdown::ANSI::Width

Extended by:
Term::ANSIColor
Includes:
Term::ANSIColor
Included in:
Kramdown::ANSI
Defined in:
lib/kramdown/ansi/width.rb

Overview

A module that provides functionality for calculating terminal width percentages and wrapping/truncating text accordingly.

The Width module includes methods to determine the available terminal width based on a percentage, and provides text wrapping and truncation capabilities that respect terminal dimensions.

Examples:

Wrapping text to 80% of terminal width

Kramdown::ANSI::Width.wrap("This is a long line of text", percentage: 80)

Truncating text to 50 characters

Kramdown::ANSI::Width.truncate("This is a long line of text", length: 50)

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.display_width(line) ⇒ Integer

Calculates the display width of a string after removing ANSI escape sequences.

Parameters:

  • line (String)

    the string to measure

Returns:

  • (Integer)

    the number of columns the string occupies in a terminal



43
44
45
# File 'lib/kramdown/ansi/width.rb', line 43

def display_width(line)
  Unicode::DisplayWidth.of(remove_ansi_escapes(line))
end

.remove_ansi_escapes(line) ⇒ String

Removes ANSI escape sequences from the given string.

Parameters:

  • line (String)

    the string potentially containing ANSI escape codes.

Returns:

  • (String)

    the string with all ANSI escape sequences removed.



34
35
36
# File 'lib/kramdown/ansi/width.rb', line 34

def remove_ansi_escapes(line)
  line.to_s.gsub(/\e\[.*?m|\e\].*?(\e|\a)\\?/, '')
end

.truncate(text, percentage: nil, length: nil, ellipsis: ?…) ⇒ String

Truncates a given string to a specified length or percentage. If the text is longer an ellipsis sequence is added at the end of the generated string, to indicate that a truncation has been performed.

Parameters:

  • text (String)

    the input string to truncate

  • percentage (Hash) (defaults to: nil)

    a customizable set of options

  • length (Hash) (defaults to: nil)

    a customizable set of options

  • ellipsis (Hash) (defaults to: ?…)

    a customizable set of options

Options Hash (percentage:):

  • the (Numeric)

    percentage value for the width (defaults to nil)

Options Hash (length:):

  • the (Integer)

    character length for the width (defaults to nil)

Options Hash (ellipsis:):

  • the (Character)

    truncation indicator (defaults to ?…)

Returns:

  • (String)

    the truncated string

Raises:

  • (ArgumentError)

    if neither percentage nor length is provided



104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/kramdown/ansi/width.rb', line 104

def truncate(text, percentage: nil, length: nil, ellipsis: ?…)
  percentage.nil? ^ length.nil? or
    raise ArgumentError, "either pass percentage or length argument"
  percentage and length ||= width(percentage:)
  ellipsis_length = ellipsis.size
  if length < ellipsis_length
    +''
  elsif text.size >= length + ellipsis_length
    text[0, length - ellipsis_length] + ellipsis
  else
    text
  end
end

.width(percentage: 100.0) ⇒ Integer

Returns the width of the terminal in characters, given a percentage.

Parameters:

  • percentage (Numeric) (defaults to: 100.0)

    the percentage value (defaults to 100.0)

Returns:

  • (Integer)

    the calculated width



26
27
28
# File 'lib/kramdown/ansi/width.rb', line 26

def width(percentage: 100.0)
  ((Float(percentage) * Tins::Terminal.columns) / 100).floor
end

.wrap(text, percentage: nil, length: nil) ⇒ String

Wraps a string to a given width, either by percentage of terminal width or by an explicit character length.

Examples:

Kramdown::ANSI::Width.wrap("Hello world!", percentage: 50)
# => "Hello\nworld!"

Parameters:

  • text (String)

    the text to wrap

  • percentage (Hash) (defaults to: nil)

    a customizable set of options

  • length (Hash) (defaults to: nil)

    a customizable set of options

Options Hash (percentage:):

  • the (Numeric)

    percentage of terminal width to use

Options Hash (length:):

  • the (Integer)

    explicit character width to wrap to

Returns:

  • (String)

    the wrapped string, preserving ANSI escape sequences

Raises:

  • (ArgumentError)

    if neither percentage nor length is provided



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
# File 'lib/kramdown/ansi/width.rb', line 58

def wrap(text, percentage: nil, length: nil)
  percentage.nil? ^ length.nil? or
    raise ArgumentError, "either pass percentage or length argument"
  percentage and length ||= width(percentage:)
  return text if length <= 0
  begin
    length = length.to_i
  rescue FloatDomainError
    return text
  end
  length = length.clamp(1..)
  length_without_ansi = 0
  result = ''
  text.gsub(/(?<nws>\S+)|(?<ws>\s)/) do
    token_value  = $&
    token_length = display_width(token_value)
    if $~[:nws]
      if length_without_ansi + token_length > length
        length_without_ansi = token_length
        if result[-1] == ' '
          result[-1, 1] = "\n#{token_value}"
        else
          result << "\n#{token_value}"
        end
      else
        length_without_ansi += token_length
        result << token_value
      end
    else
      length_without_ansi += token_length
      result << token_value
    end
  end
  result
end

Instance Method Details

#display_width(line) ⇒ Integer (private)

Calculates the display width of a string after removing ANSI escape sequences.

Parameters:

  • line (String)

    the string to measure

Returns:

  • (Integer)

    the number of columns the string occupies in a terminal



43
44
45
# File 'lib/kramdown/ansi/width.rb', line 43

def display_width(line)
  Unicode::DisplayWidth.of(remove_ansi_escapes(line))
end

#remove_ansi_escapes(line) ⇒ String (private)

Removes ANSI escape sequences from the given string.

Parameters:

  • line (String)

    the string potentially containing ANSI escape codes.

Returns:

  • (String)

    the string with all ANSI escape sequences removed.



34
35
36
# File 'lib/kramdown/ansi/width.rb', line 34

def remove_ansi_escapes(line)
  line.to_s.gsub(/\e\[.*?m|\e\].*?(\e|\a)\\?/, '')
end

#truncate(text, percentage: nil, length: nil, ellipsis: ?…) ⇒ String (private)

Truncates a given string to a specified length or percentage. If the text is longer an ellipsis sequence is added at the end of the generated string, to indicate that a truncation has been performed.

Parameters:

  • text (String)

    the input string to truncate

Options Hash (percentage:):

  • the (Numeric)

    percentage value for the width (defaults to nil)

Options Hash (length:):

  • the (Integer)

    character length for the width (defaults to nil)

Options Hash (ellipsis:):

  • the (Character)

    truncation indicator (defaults to ?…)

Returns:

  • (String)

    the truncated string

Raises:

  • (ArgumentError)

    if neither percentage nor length is provided



104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/kramdown/ansi/width.rb', line 104

def truncate(text, percentage: nil, length: nil, ellipsis: ?…)
  percentage.nil? ^ length.nil? or
    raise ArgumentError, "either pass percentage or length argument"
  percentage and length ||= width(percentage:)
  ellipsis_length = ellipsis.size
  if length < ellipsis_length
    +''
  elsif text.size >= length + ellipsis_length
    text[0, length - ellipsis_length] + ellipsis
  else
    text
  end
end

#width(percentage: 100.0) ⇒ Integer (private)

Returns the width of the terminal in characters, given a percentage.

Parameters:

  • percentage (Numeric) (defaults to: 100.0)

    the percentage value (defaults to 100.0)

Returns:

  • (Integer)

    the calculated width



26
27
28
# File 'lib/kramdown/ansi/width.rb', line 26

def width(percentage: 100.0)
  ((Float(percentage) * Tins::Terminal.columns) / 100).floor
end

#wrap(text, percentage: nil, length: nil) ⇒ String (private)

Wraps a string to a given width, either by percentage of terminal width or by an explicit character length.

Examples:

Kramdown::ANSI::Width.wrap("Hello world!", percentage: 50)
# => "Hello\nworld!"

Parameters:

  • text (String)

    the text to wrap

Options Hash (percentage:):

  • the (Numeric)

    percentage of terminal width to use

Options Hash (length:):

  • the (Integer)

    explicit character width to wrap to

Returns:

  • (String)

    the wrapped string, preserving ANSI escape sequences

Raises:

  • (ArgumentError)

    if neither percentage nor length is provided



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
# File 'lib/kramdown/ansi/width.rb', line 58

def wrap(text, percentage: nil, length: nil)
  percentage.nil? ^ length.nil? or
    raise ArgumentError, "either pass percentage or length argument"
  percentage and length ||= width(percentage:)
  return text if length <= 0
  begin
    length = length.to_i
  rescue FloatDomainError
    return text
  end
  length = length.clamp(1..)
  length_without_ansi = 0
  result = ''
  text.gsub(/(?<nws>\S+)|(?<ws>\s)/) do
    token_value  = $&
    token_length = display_width(token_value)
    if $~[:nws]
      if length_without_ansi + token_length > length
        length_without_ansi = token_length
        if result[-1] == ' '
          result[-1, 1] = "\n#{token_value}"
        else
          result << "\n#{token_value}"
        end
      else
        length_without_ansi += token_length
        result << token_value
      end
    else
      length_without_ansi += token_length
      result << token_value
    end
  end
  result
end