Module: IS::Term::StringHelpers

Defined in:
lib/is-term/string_helpers.rb

Overview

Terminal string rendering utilities with proper width calculation for Unicode characters, emoji, East Asian characters, and ANSI escape sequences.

This module provides essential string manipulation methods for terminal UIs, handling complex Unicode display widths correctly while ignoring ANSI color/style codes. It extends String via Refinements to provide a clean, chainable API.

Features

  • Correct width calculation (emoji=2, CJK=2, ASCII=1, ANSI=0)

  • Safe truncation preserving ANSI codes

  • Flexible alignment (left, right, center)

  • Ellipsis with configurable marker

Usage

require 'is-term/string_helpers'

Standalone (no refinements, no includes):

IS::Term::StringHelpers.str_width("中👨‍⚕️")
# => 4

Include private methods:

include IS::Term::StringHelpers
str_width("中👨‍⚕️")
# => 4

With refinements (recommended):

using IS::Term::StringHelpers
"中👨‍⚕️".width       # => 4
"中👨‍⚕️".ellipsis(3) # => "中…"

Constant Summary collapse

ALIGN_LEFT =
:left
ALIGN_RIGHT =
:right
ALIGN_CENTER =
:center
ALIGN_MODES =
[ ALIGN_LEFT, ALIGN_RIGHT, ALIGN_CENTER ].freeze
DEFAULT_ELLIPSIS_MARKER =
''
DEFAULT_ALIGN_MODE =
ALIGN_LEFT

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.str_align(str, width, mode = DEFAULT_ALIGN_MODE) ⇒ String

Aligns string within specified display width using spaces.

Returns original string if source width is greater than or equal to target. Supports left, right, and center alignment.

Examples:

IS::Term::StringHelpers.str_align("hi", 6)          # => "hi    "
IS::Term::StringHelpers.str_align("hi", 6, :right)  # => "    hi"
IS::Term::StringHelpers.str_align("hi", 6, :center) # => "  hi  "

Parameters:

  • str (String)

    Input string

  • width (Integer)

    Target display width

  • mode (:left, :right, :center) (defaults to: DEFAULT_ALIGN_MODE)

    Alignment mode

Returns:

  • (String)

    Aligned string padded with spaces

Raises:

  • ArgumentError if invalid alignment mode



159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
# File 'lib/is-term/string_helpers.rb', line 159

def str_align str, width, mode = DEFAULT_ALIGN_MODE
  src_width = str_width str
  return str if src_width >= width
  case mode
  when ALIGN_LEFT
    str + ' ' * (width - src_width)
  when ALIGN_RIGHT
    ' ' * (width - src_width) + str
  when ALIGN_CENTER
    left = (width - src_width) / 2
    right = width - src_width - left
    ' ' * left + str + ' ' * right
  else
    raise ArgumentError, "Invalid align value: #{ mode.inspect }", caller_locations
  end
end

.str_ellipsis(str, width, marker = DEFAULT_ELLIPSIS_MARKER) ⇒ String

Truncates string to fit within width, appending ellipsis marker if truncated.

Raises ArgumentError if marker display width exceeds target width. Preserves ANSI codes and handles Unicode widths correctly.

Examples:

IS::Term::StringHelpers.str_ellipsis("中ABC", 3)  # => "中…"
IS::Term::StringHelpers.str_ellipsis("short", 10) # => "short"

Parameters:

  • str (String)

    Input string

  • width (Integer)

    Target display width

  • marker (String) (defaults to: DEFAULT_ELLIPSIS_MARKER)

    Ellipsis marker (default: )

Returns:

  • (String)

    Truncated string with marker or original string

Raises:

  • ArgumentError if str_width of marker > width



135
136
137
138
139
140
141
142
143
# File 'lib/is-term/string_helpers.rb', line 135

def str_ellipsis str, width, marker = DEFAULT_ELLIPSIS_MARKER
  marker_width = str_width marker
  raise ArgumentError, "Marker too long: #{ marker.inspect }", caller_locations if marker_width > width
  if str_width(str) > width
    str_truncate(str, width - marker_width) + marker
  else
    str
  end
end

.str_truncate(str, width) ⇒ String

Truncates string to specified display width, preserving ANSI escape sequences.

ANSI codes are fully skipped (width 0), truncation occurs only on visible characters. Returns original string if already within width or empty string for non-positive widths.

Examples:

IS::Term::StringHelpers.str_truncate("中ABC", 2)            # => "中"
IS::Term::StringHelpers.str_truncate("\e[31mHello\e[0m", 3) # => "\e[31mHel"

Parameters:

  • str (String)

    Input string

  • width (Integer)

    Maximum display width

Returns:

  • (String)

    Truncated string (may be empty)

Raises:

  • ArgumentError if width is invalid



101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/is-term/string_helpers.rb', line 101

def str_truncate str, width
  return str if str.length <= width
  return '' if width <= 0
  current = 0
  position = 0
  str.scan(TOKEN) do |match|
    w = case match
    when ESC_CODES
      0
    when EMOJI, EAST_ASIA
      2
    else
      1
    end
    current += w
    return str[0, position] if current > width
    position += match.length
  end
  str
end

.str_width(str) ⇒ Integer

Calculates the display width of a string in terminal context.

Handles Unicode display rules:

  • ANSI escape sequences: width 0

  • Emoji: width 2

  • East Asian characters (Han, Hiragana, Katakana, Hangul): width 2

  • Other characters: width 1

Examples:

IS::Term::StringHelpers.str_width("中👨‍⚕️A")         # => 5
IS::Term::StringHelpers.str_width("\e[31mHi\e[0m") # => 2

Parameters:

  • str (String)

    Input string

Returns:

  • (Integer)

    Display width in columns



72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/is-term/string_helpers.rb', line 72

def str_width str
  current = 0
  str.scan(TOKEN) do |match|
    w = case match
    when ESC_CODES
      0
    when EMOJI, EAST_ASIA
      2
    else
      1
    end
    current += w
  end
  current
end

Instance Method Details

#str_align(str, width, mode = DEFAULT_ALIGN_MODE) ⇒ String (private)

Aligns string within specified display width using spaces.

Returns original string if source width is greater than or equal to target. Supports left, right, and center alignment.

Examples:

IS::Term::StringHelpers.str_align("hi", 6)          # => "hi    "
IS::Term::StringHelpers.str_align("hi", 6, :right)  # => "    hi"
IS::Term::StringHelpers.str_align("hi", 6, :center) # => "  hi  "

Parameters:

  • str (String)

    Input string

  • width (Integer)

    Target display width

  • mode (:left, :right, :center) (defaults to: DEFAULT_ALIGN_MODE)

    Alignment mode

Returns:

  • (String)

    Aligned string padded with spaces

Raises:

  • ArgumentError if invalid alignment mode



159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
# File 'lib/is-term/string_helpers.rb', line 159

def str_align str, width, mode = DEFAULT_ALIGN_MODE
  src_width = str_width str
  return str if src_width >= width
  case mode
  when ALIGN_LEFT
    str + ' ' * (width - src_width)
  when ALIGN_RIGHT
    ' ' * (width - src_width) + str
  when ALIGN_CENTER
    left = (width - src_width) / 2
    right = width - src_width - left
    ' ' * left + str + ' ' * right
  else
    raise ArgumentError, "Invalid align value: #{ mode.inspect }", caller_locations
  end
end

#str_ellipsis(str, width, marker = DEFAULT_ELLIPSIS_MARKER) ⇒ String (private)

Truncates string to fit within width, appending ellipsis marker if truncated.

Raises ArgumentError if marker display width exceeds target width. Preserves ANSI codes and handles Unicode widths correctly.

Examples:

IS::Term::StringHelpers.str_ellipsis("中ABC", 3)  # => "中…"
IS::Term::StringHelpers.str_ellipsis("short", 10) # => "short"

Parameters:

  • str (String)

    Input string

  • width (Integer)

    Target display width

  • marker (String) (defaults to: DEFAULT_ELLIPSIS_MARKER)

    Ellipsis marker (default: )

Returns:

  • (String)

    Truncated string with marker or original string

Raises:

  • ArgumentError if str_width of marker > width



135
136
137
138
139
140
141
142
143
# File 'lib/is-term/string_helpers.rb', line 135

def str_ellipsis str, width, marker = DEFAULT_ELLIPSIS_MARKER
  marker_width = str_width marker
  raise ArgumentError, "Marker too long: #{ marker.inspect }", caller_locations if marker_width > width
  if str_width(str) > width
    str_truncate(str, width - marker_width) + marker
  else
    str
  end
end

#str_truncate(str, width) ⇒ String (private)

Truncates string to specified display width, preserving ANSI escape sequences.

ANSI codes are fully skipped (width 0), truncation occurs only on visible characters. Returns original string if already within width or empty string for non-positive widths.

Examples:

IS::Term::StringHelpers.str_truncate("中ABC", 2)            # => "中"
IS::Term::StringHelpers.str_truncate("\e[31mHello\e[0m", 3) # => "\e[31mHel"

Parameters:

  • str (String)

    Input string

  • width (Integer)

    Maximum display width

Returns:

  • (String)

    Truncated string (may be empty)

Raises:

  • ArgumentError if width is invalid



101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/is-term/string_helpers.rb', line 101

def str_truncate str, width
  return str if str.length <= width
  return '' if width <= 0
  current = 0
  position = 0
  str.scan(TOKEN) do |match|
    w = case match
    when ESC_CODES
      0
    when EMOJI, EAST_ASIA
      2
    else
      1
    end
    current += w
    return str[0, position] if current > width
    position += match.length
  end
  str
end

#str_width(str) ⇒ Integer (private)

Calculates the display width of a string in terminal context.

Handles Unicode display rules:

  • ANSI escape sequences: width 0

  • Emoji: width 2

  • East Asian characters (Han, Hiragana, Katakana, Hangul): width 2

  • Other characters: width 1

Examples:

IS::Term::StringHelpers.str_width("中👨‍⚕️A")         # => 5
IS::Term::StringHelpers.str_width("\e[31mHi\e[0m") # => 2

Parameters:

  • str (String)

    Input string

Returns:

  • (Integer)

    Display width in columns



72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/is-term/string_helpers.rb', line 72

def str_width str
  current = 0
  str.scan(TOKEN) do |match|
    w = case match
    when ESC_CODES
      0
    when EMOJI, EAST_ASIA
      2
    else
      1
    end
    current += w
  end
  current
end