Module: Terminal::Ansi

Defined in:
lib/terminal/ansi.rb,
lib/terminal/ansi/named_colors.rb

Overview

Fast ANSI control code and BBCode processing.

Class Attribute Summary collapse

ANSI control code generator functions collapse

BBcode related functions collapse

Other tool functions collapse

Cursor manipulation collapse

Screen manipulation collapse

Other ANSI control functions collapse

Class Attribute Details

.attributesArray<Symbol> (readonly)

Supported attribute names.

Returns:

  • (Array<Symbol>)

    all attribute names

See Also:



15
# File 'lib/terminal/ansi.rb', line 15

def attributes = @attributes.dup

.colorsArray<Symbol> (readonly)

Supported 3/4-bit color names.

Returns:

  • (Array<Symbol>)

    all color names

See Also:



23
# File 'lib/terminal/ansi.rb', line 23

def colors = @colors.dup

.named_colorsArray<Symbol> (readonly)

Supported basic 24-bit (Kitty compatible) color names.

Returns:

  • (Array<Symbol>)

    all basic named_colors names

See Also:



31
# File 'lib/terminal/ansi.rb', line 31

def named_colors = NAMED_COLORS.keys.map!(&:to_sym)

Class Method Details

.[](*attributes) ⇒ String

Combine given attributes, colors, named_colors and color codes to an ANSI control code sequence.

Colors can specified by their name for ANSI 3-bit and 4-bit colors. For 8-bit ANSI colors use 2-digit hexadecimal values 00...ff.

To use RGB ANSI colors (24-bit colors) specify 3-digit or 6-digit hexadecimal values 000...fff or 000000...ffffff. This represent the RRGGBB values (or RGB for short version) like you may known from CSS color notation.

To use a color as background color prefix the color attribute with bg_ or on_. To use a color as underline color prefix the color attribute with ul_. To clarify that a color attribute have to be used as foreground color use the prefix fg_.

Examples:

Valid Foreground Color Attributes

Terminal::Ansi[:yellow]
Terminal::Ansi[:fg_fab]
Terminal::Ansi[:fg_00aa00]
Terminal::Ansi[:af]
Terminal::Ansi[:fg_af]
Terminal::Ansi['#fab']
Terminal::Ansi['#00aa00']
Terminal::Ansi['lightblue']

Valid Background Color Attributes

Terminal::Ansi[:bg_yellow]
Terminal::Ansi[:bg_fab]
Terminal::Ansi[:bg_00aa00]
Terminal::Ansi[:bg_af]
Terminal::Ansi['bg#00aa00']
Terminal::Ansi['bg_lightblue']

Terminal::Ansi[:on_yellow]
Terminal::Ansi[:on_fab]
Terminal::Ansi[:on_00aa00]
Terminal::Ansi[:on_af]
Terminal::Ansi['on#00aa00']
Terminal::Ansi['on_lightblue']

Valid Underline Color Attributes

Terminal::Ansi[:underline, :ul_yellow]
Terminal::Ansi[:underline, :ul_fab]
Terminal::Ansi[:underline, :ul_00aa00]
Terminal::Ansi[:underline, :ul_fa]
Terminal::Ansi[:underline, :ul_bright_yellow]
Terminal::Ansi[:underline, 'ul#00aa00']
Terminal::Ansi['underline', 'ul_lightblue']

Combined attributes:

Terminal::Ansi[:bold, :italic, :bright_white, :on_0000cc]

Parameters:

  • attributes (Array<Symbol, String>)

    attribute names to be used

Returns:

  • (String)

    combined ANSI attributes

See Also:



95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# File 'lib/terminal/ansi.rb', line 95

def [](*attributes)
  return +'' if attributes.empty?
  "\e[#{
    attributes
      .map! do |arg|
        case arg
        when String
          @attr_map[arg] || _invalid(arg)
        when Symbol
          @attrs_map[arg] || _invalid(arg)
        when (0..255)
          "38;5;#{arg}"
        when (256..511)
          "48;5;#{arg - 256}"
        when (512..767)
          "58;5;#{arg - 512}"
        else
          _invalid(arg)
        end
      end
      .join(';')
  }m"
end

.ansi?(str) ⇒ true, false

Test if given String contains ANSI control codes.

Parameters:

  • str (#to_s)

    object to be tested

Returns:

  • (true, false)

    whether if attributes are found



123
# File 'lib/terminal/ansi.rb', line 123

def ansi?(str) = @re_test.match?(str.to_s)

.bbcode(str) ⇒ String

Replace embedded BBCode-like attributes with ANSI control codes.

Examples:

Terminal::Ansi.bbcode "[b]Bold[/b] Text"
# => "\e[1mBold\e[22m Text"

Parameters:

  • str (#to_s)

    string to be modified

Returns:

  • (String)

    string with ANSI attributes

See Also:



219
220
221
222
223
224
225
226
227
# File 'lib/terminal/ansi.rb', line 219

def bbcode(str)
  str = str.to_s
  return str.dup unless str.index('[')
  str.gsub(@re_bbcode) do |match_str|
    match = Regexp.last_match(1) or next match_str
    next "[#{match[1..]}]" if match[0] == '\\'
    try_convert(match) || match_str
  end
end

.char_repeat(count = 1) ⇒ String

Repeat last char.

Parameters:

  • count (Integer) (defaults to: 1)

    repeat count

Returns:

  • (String)

    ANSI control code



447
# File 'lib/terminal/ansi.rb', line 447

def char_repeat(count = 1) = "\e[#{count}b"

.cursor_back(columns = 1) ⇒ String

Move cursor given columns back.

Returns:

  • (String)

    ANSI control code



328
# File 'lib/terminal/ansi.rb', line 328

def cursor_back(columns = 1) = "\e[#{columns}D"

.cursor_column(column = 1) ⇒ String

Move cursor to given column in the current row.

Parameters:

  • column (Integer) (defaults to: 1)

    column index

Returns:

  • (String)

    ANSI control code



346
# File 'lib/terminal/ansi.rb', line 346

def cursor_column(column = 1) = "\e[#{column}G"

.cursor_column_rel(column = 1) ⇒ String

Move cursor to given column in the current row relative to the current position. (Skip some columns.)

Parameters:

  • column (Integer) (defaults to: 1)

    column index

Returns:

  • (String)

    ANSI control code



354
# File 'lib/terminal/ansi.rb', line 354

def cursor_column_rel(column = 1) = "\e[#{column}a"

.cursor_down(lines = 1) ⇒ String

Move cursor given lines down.

Parameters:

  • lines (Integer) (defaults to: 1)

    number of lines to move

Returns:

  • (String)

    ANSI control code



316
# File 'lib/terminal/ansi.rb', line 316

def cursor_down(lines = 1) = "\e[#{lines}B"

.cursor_forward(columns = 1) ⇒ String

Move cursor given columns forward.

Parameters:

  • columns (Integer) (defaults to: 1)

    number of columns to move

Returns:

  • (String)

    ANSI control code



322
# File 'lib/terminal/ansi.rb', line 322

def cursor_forward(columns = 1) = "\e[#{columns}C"

.cursor_hideString

Hide cursor.

Returns:

  • (String)

    ANSI control code



381
# File 'lib/terminal/ansi.rb', line 381

def cursor_hide = +CURSOR_HIDE

.cursor_next_line(lines = 1) ⇒ String

Move cursor to the beginning of the given next line.

Returns:

  • (String)

    ANSI control code



334
# File 'lib/terminal/ansi.rb', line 334

def cursor_next_line(lines = 1) = "\e[#{lines}E"

.cursor_pos(row, column = nil) ⇒ String

Move to given row and column.

Parameters:

  • row (Integer)

    row index

  • column (Integer) (defaults to: nil)

    column index

Returns:

  • (String)

    ANSI control code



368
369
370
371
# File 'lib/terminal/ansi.rb', line 368

def cursor_pos(row, column = nil)
  return column ? "\e[;#{column}H" : "\e[H" unless row
  column ? "\e[#{row};#{column}H" : "\e[#{row}H"
end

.cursor_prev_line(lines = 1) ⇒ String

Move cursor to the beginning of the given previous line.

Returns:

  • (String)

    ANSI control code



340
# File 'lib/terminal/ansi.rb', line 340

def cursor_prev_line(lines = 1) = "\e[#{lines}F"

.cursor_restore_posString

Restore saved cursor position.

Returns:

  • (String)

    ANSI control code



391
# File 'lib/terminal/ansi.rb', line 391

def cursor_restore_pos = +CURSOR_POS_RESTORE

.cursor_row_rel(row = 1) ⇒ String

Move cursor to given row relative to the current position. (Skip some rows.)

Parameters:

  • row (Integer) (defaults to: 1)

    row index

Returns:

  • (String)

    ANSI control code



361
# File 'lib/terminal/ansi.rb', line 361

def cursor_row_rel(row = 1) = "\e[#{row}e"

.cursor_save_posString

Save current cursor position.

Returns:

  • (String)

    ANSI control code



386
# File 'lib/terminal/ansi.rb', line 386

def cursor_save_pos = +CURSOR_POS_SAVE

.cursor_showString

Show cursor.

Returns:

  • (String)

    ANSI control code



376
# File 'lib/terminal/ansi.rb', line 376

def cursor_show = +CURSOR_SHOW

.cursor_up(lines = 1) ⇒ String

Move cursor given lines up.

Parameters:

  • lines (Integer) (defaults to: 1)

    number of lines to move

Returns:

  • (String)

    ANSI control code



310
# File 'lib/terminal/ansi.rb', line 310

def cursor_up(lines = 1) = "\e[#{lines}A"

.decorate(str, *attributes, reset: true) ⇒ String

Decorate given argument with ANSI attributes and colors.

Examples:

Terminal::Ansi.decorate(
  'Hello World!',
  :bold, :italic, :bright_white, :on_00c
)
# => "\e[1;3;97;48;2;0;0;204mHello World!\e[m"

Parameters:

  • str (#to_s)

    object to be decorated

  • attributes (Array<Symbol, String>)

    attribute names to be used

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

    whether to include reset code for ANSI attributes

Returns:

  • (String)

    str converted and decorated with the ANSI attributes

See Also:



141
142
143
144
# File 'lib/terminal/ansi.rb', line 141

def decorate(str, *attributes, reset: true)
  attributes = self[*attributes]
  attributes.empty? ? "#{str}" : "#{attributes}#{str}#{"\e[m" if reset}"
end

.line_erase(part = :all) ⇒ String

Erase part of line.

Parameters:

  • part (:to_end, :to_start, :all) (defaults to: :all)

    line part to erase

Returns:

  • (String)

    ANSI control code



453
# File 'lib/terminal/ansi.rb', line 453

def line_erase(part = :all) = "\e[#{@line_erase[part]}K"

Create a hyperlink. This is not widely supported; works for Ghostty, iTerm2, Kitty, Rio, Tabby, WezTerm.

Parameters:

  • url (#to_s)

    URL to link to

  • text (#to_s)

    text to display for the link

Returns:

  • (String)

    ANSI control code



480
# File 'lib/terminal/ansi.rb', line 480

def link(url, text) = "\e]8;;#{url}\a#{text}\e]8;;\a"

.notify(text) ⇒ String

Show a simple notification. This is not widely supported; works for Ghostty, iTerm2, Kitty, WezTerm.

Parameters:

  • text (#to_s)

    text to display

Returns:

  • (String)

    ANSI control code



491
# File 'lib/terminal/ansi.rb', line 491

def notify(text) = "\e]9;#{text}\a"

.plain(str) ⇒ String

Remove any BBCode-like and/or ANSI attributes.

Parameters:

  • str (#to_s)

    string to be modified

Returns:

  • (String)

    string without BBCode and ANSI control codes.

See Also:



264
265
266
267
268
269
270
271
272
273
274
275
276
277
# File 'lib/terminal/ansi.rb', line 264

def plain(str)
  unless (str = str.to_s).index('[')
    return str.index("\e") ? str.gsub(@re_test, '') : str.dup
  end
  str =
    str.gsub(@re_bbcode) do |match_str|
      match = Regexp.last_match(1) or next match_str
      next "[#{match[1..]}]" if match[0] == '\\'
      next match_str if (match = match.split).empty?
      next if match.all? { @attr_map[_1] }
      match_str
    end
  str.index("\e") ? str.gsub!(@re_test, '') : str
end

.progress(state, percent = 0) ⇒ String

Set task progress state. This is not widely supported; works for Ghostty, iTerm2, Kitty.

Parameters:

  • state (:show, :warning, :error, :indeterminate, :hide)
    • :show show progress indicator with given percent value in default mode
    • :warning show progress with given percent value in warning mode
    • :error show progress with given percent value in error mode
    • :indeterminate show progress in indeterminate state (percent value is ignored)
    • :hide hide progress indicator (percent value is ignored)

Returns:

  • (String)

    ANSI control code



511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
# File 'lib/terminal/ansi.rb', line 511

def progress(state, percent = 0)
  case state
  when :show, true
    state = 1
  when :err, :error
    state = 2
  when :warn, :warning
    state = 4
  when Numeric
    percent, state = state, 1
  when :indeterminate
    return +PROGRESS_SHOW_INDETERMINATE
  else
    return +PROGRESS_HIDE
  end
  "\e]9;4;#{state};#{percent.to_i.clamp(0, 100)}\a"
end

.rainbow(str, frequency: 0.3, spread: 0.8, seed: 1.1) ⇒ String

Create nice colored text.

Parameters:

  • str (#to_s)

    string to enrich with color

  • frequency (Float) (defaults to: 0.3)

    color change frequency

  • spread (Float) (defaults to: 0.8)

    number of chars with same color

  • seed (Float) (defaults to: 1.1)

    start index on sinus curve

Returns:

  • (String)

    fancy text



286
287
288
289
290
291
292
293
294
295
296
297
298
# File 'lib/terminal/ansi.rb', line 286

def rainbow(str, frequency: 0.3, spread: 0.8, seed: 1.1)
  pos = -1
  @pi2_third ||= 2.0 * Math::PI / 3.0
  @pi4_third ||= 4.0 * Math::PI / 3.0
  (
    str.to_s.chars.map! do |char|
      i = (seed + ((pos += 1) / spread)) * frequency
      "\e[38;2;#{(Math.sin(i) * 255).to_i.abs};" \
        "#{(Math.sin(i + @pi2_third) * 255).to_i.abs};" \
        "#{(Math.sin(i + @pi4_third) * 255).to_i.abs}m#{char}"
    end << RESET
  ).join
end

.scale(text, scale: nil, width: nil, fracn: nil, fracd: nil, vertical: nil, horizontal: nil) ⇒ String

Create scaled text. It uses the text sizing protocol. This is not widely supported; works for Kitty.

Examples:

Double-height Greeting

Terminal::Ansi.scale('Hello Ruby!', scale: 2)

Half-height Greeting

Terminal::Ansi.scale('Hello Ruby!', fracn: 1, fracd: 2, vertical: :centered)

Parameters:

  • text (#to_s)

    text to scale

  • scale (Integer, nil) (defaults to: nil)

    overall scale size, range 1..7

  • width (Integer, nil) (defaults to: nil)

    with in cells, range 0..7

  • fracn (Integer, nil) (defaults to: nil)

    numerator for the fractional scale, range: 0..15

  • fracd (Integer, nil) (defaults to: nil)

    denominator for the fractional scale, range: 0..15, > fracn

  • vertical (:top, :bottom, :centered, nil) (defaults to: nil)

    vertical alignment to use for fractionally scaled text

  • horizontal (:left, :right, :centered, nil) (defaults to: nil)

    horizontal alignment to use for fractionally scaled text

Returns:

  • (String)

    ANSI control code



555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
# File 'lib/terminal/ansi.rb', line 555

def scale(
  text,
  scale: nil,
  width: nil,
  fracn: nil,
  fracd: nil,
  vertical: nil,
  horizontal: nil
)
  opts = scale ? ["s=#{scale.clamp(1, 7)}"] : []
  opts << "w=#{width.clamp(0, 7)}" if width
  if fracn
    opts << "n=#{fracn = fracn.clamp(0, 15)}"
    opts << "d=#{fracd.clamp(fracn + 1, 15)}" if fracd
    case vertical
    when 0, :top
      opts << 'v=0'
    when 1, :bottom
      opts << 'v=1'
    when 2, :centered
      opts << 'v=2'
    end
    case horizontal
    when 0, :left
      opts << 'h=0'
    when 1, :right
      opts << 'h=1'
    when 2, :centered
      opts << 'h=2'
    end
  end
  "\e]66;#{opts.join(':')};#{text}\a"
end

.screen_alternateString

Use alternative screen buffer.

Returns:

  • (String)

    ANSI control code



418
# File 'lib/terminal/ansi.rb', line 418

def screen_alternate = +SCREEN_ALTERNATE

.screen_alternate_offString

Do not longer use alternative screen buffer.

Returns:

  • (String)

    ANSI control code



423
# File 'lib/terminal/ansi.rb', line 423

def screen_alternate_off = +SCREEN_ALTERNATE_OFF

.screen_erase(part = :all) ⇒ String

Erase screen part.

Parameters:

  • part (:below, :above, :all, :scrollback) (defaults to: :all)

    screen part to erase

Returns:

  • (String)

    ANSI control code



403
# File 'lib/terminal/ansi.rb', line 403

def screen_erase(part = :all) = "\e[#{@screen_erase[part]}J"

.screen_restoreString

Restore current screen.

Returns:

  • (String)

    ANSI control code



413
# File 'lib/terminal/ansi.rb', line 413

def screen_restore = +SCREEN_RESTORE

.screen_saveString

Safe current screen.

Returns:

  • (String)

    ANSI control code



408
# File 'lib/terminal/ansi.rb', line 408

def screen_save = +SCREEN_SAVE

.screen_scroll_down(lines = 1) ⇒ String

Scroll window given lines down.

Returns:

  • (String)

    ANSI control code



435
# File 'lib/terminal/ansi.rb', line 435

def screen_scroll_down(lines = 1) = "\e[#{lines}T"

.screen_scroll_up(lines = 1) ⇒ String

Scroll window given lines up.

Parameters:

  • lines (Integer) (defaults to: 1)

    number of lines to scroll

Returns:

  • (String)

    ANSI control code



429
# File 'lib/terminal/ansi.rb', line 429

def screen_scroll_up(lines = 1) = "\e[#{lines}S"

.title(title) ⇒ String

Set (tab) title. This is not widely supported; works for Hyper, iTerm2, Kitty, MacOS Terminal, Tabby, WezTerm.

Parameters:

  • title (#to_s)

    text

Returns:

  • (String)

    ANSI control code



466
# File 'lib/terminal/ansi.rb', line 466

def title(title) = "\e]0;#{title}\a"

.try_convert(attributes, separator: ' ') ⇒ String?

Try to combine given ANSI attributes and colors. The attributes and colors have to be separated by given separator`.

Examples:

Valid Attribute String

Terminal::Ansi.try_convert('bold italic blink red on#00ff00')
# => "\e[1;3;5;31;48;2;0;255;0m"

Invalid Attribute String

Terminal::Ansi.try_convert('cool bold on green')
# => nil

Parameters:

  • attributes (#to_s)

    attributes separated by given separator

  • separator (String) (defaults to: ' ')

    attribute separator char

Returns:

  • (String)

    combined ANSI attributes

  • (nil)

    when string does not contain valid attributes

See Also:



177
178
179
180
181
# File 'lib/terminal/ansi.rb', line 177

def try_convert(attributes, separator: ' ')
  return unless attributes
  return if (attributes = attributes.to_s.split(separator)).empty?
  "\e[#{attributes.map! { @attr_map[_1] || return }.join(';')}m"
end

.unbbcode(str) ⇒ String

Remove embedded BBCode-like attributes.

Examples:

Terminal::Ansi.unbbcode "[b]Bold[/b] Text"
# => "Bold Text"

Parameters:

  • str (#to_s)

    string to be modified

Returns:

  • (String)

    string without BBCode

See Also:



239
240
241
242
243
244
245
246
247
248
249
# File 'lib/terminal/ansi.rb', line 239

def unbbcode(str)
  str = str.to_s
  return str.dup unless str.index('[')
  str.gsub(@re_bbcode) do |match_str|
    match = Regexp.last_match(1) or next match_str
    next "[#{match[1..]}]" if match[0] == '\\'
    next match_str if (match = match.split).empty?
    next if match.all? { @attr_map[_1] }
    match_str
  end
end

.undecorate(str) ⇒ String

Remove ANSI functions, attributes and colors from given string.

Examples:

Terminal::Ansi.undecorate("\e[1;3;97;48;2;0;0;204mHello World!\e[m")
# => "Hello World!"

Parameters:

  • str (#to_s)

    string to be modified

Returns:

  • (String)

    string without ANSI attributes

See Also:



156
157
158
# File 'lib/terminal/ansi.rb', line 156

def undecorate(str)
  (str = str.to_s).index("\e") ? str.gsub(@re_test, '') : str.dup
end

.valid?(*attributes) ⇒ true, false

Test if all given attributes are valid.

Parameters:

  • attributes (Array<Symbol, String>)

    attribute names to be used

Returns:

  • (true, false)

    whether if all given attributes are valid

See Also:



189
190
191
192
193
194
195
196
197
198
199
200
# File 'lib/terminal/ansi.rb', line 189

def valid?(*attributes)
  attributes.all? do |arg|
    case arg
    when String
      @attr_map[arg]
    when Symbol
      @attrs_map[arg]
    when (0..767)
      true
    end
  end
end