Module: Terminal::Ansi
- Defined in:
- lib/terminal/ansi.rb,
lib/terminal/ansi/named_colors.rb,
lib/terminal/ansi/screen_viewer.rb
Overview
ANSI escape code generation and BBCode-to-ANSI conversion.
Provides methods for text decoration, color, cursor control, screen manipulation, hyperlinks, notifications, and progress indicators. All methods return ANSI escape sequence strings — they do not write to the terminal directly.
Colors can be specified as named symbols, 256-color indices, hex strings, or CSS/X11 color names.
Defined Under Namespace
Classes: ScreenViewer
Class Attribute Summary collapse
-
.attributes ⇒ Array<Symbol>
readonly
All known text attribute names (bold, italic, underline, etc.).
-
.colors ⇒ Array<Symbol>
readonly
All known basic color names.
-
.named_colors ⇒ Array<Symbol>
readonly
All CSS/X11 named colors.
ANSI Control Code Generation collapse
-
.[](*attributes) ⇒ String
Generate an ANSI escape sequence from attribute names or color indices.
-
.char_repeat(count = 1) ⇒ String
Repeat the last printed character.
-
.cursor_back(columns = 1) ⇒ String
Move cursor left.
-
.cursor_column(column = 1) ⇒ String
Move cursor to absolute column.
-
.cursor_column_rel(column = 1) ⇒ String
Move cursor right by relative columns.
-
.cursor_down(lines = 1) ⇒ String
Move cursor down.
-
.cursor_forward(columns = 1) ⇒ String
Move cursor right.
-
.cursor_hide ⇒ String
Hide cursor.
-
.cursor_next_line(lines = 1) ⇒ String
Move cursor to beginning of line, N lines down.
-
.cursor_pos(row, column = nil) ⇒ String
Move cursor to absolute position.
-
.cursor_prev_line(lines = 1) ⇒ String
Move cursor to beginning of line, N lines up.
-
.cursor_restore_pos ⇒ String
Restore previously saved cursor position.
-
.cursor_row_rel(row = 1) ⇒ String
Move cursor down by relative rows.
-
.cursor_save_pos ⇒ String
Save current cursor position.
-
.cursor_show ⇒ String
Show cursor.
-
.cursor_up(lines = 1) ⇒ String
Move cursor up.
-
.decorate(str, *attributes, reset: true) ⇒ String
Wrap a string with ANSI escape codes.
-
.line_erase(part = :all) ⇒ String
Erase part of the current line.
-
.link(url, text, **params) ⇒ String
Create a complete hyperlink (OSC 8).
-
.link_end ⇒ String
End a hyperlink.
-
.link_start(url, **params) ⇒ String
Start a hyperlink (OSC 8).
-
.notify(text) ⇒ String
Send a desktop notification via the terminal.
-
.progress(state, percent = 0) ⇒ String
Show or update a progress indicator in the terminal tab/title bar.
-
.rainbow(str, frequency: 0.3, spread: 0.8, seed: 1.1) ⇒ String
Generate rainbow-colored text using true color (24-bit) ANSI codes.
-
.scale(text, scale: nil, width: nil, fracn: nil, fracd: nil, vertical: nil, horizontal: nil) ⇒ String
Scale text using the text sizing protocol.
-
.screen_alternate ⇒ String
Switch to the alternate screen buffer.
-
.screen_alternate_off ⇒ String
Switch back from the alternate screen buffer.
-
.screen_erase(part = :all) ⇒ String
Erase part of the screen.
-
.screen_restore ⇒ String
Restore previously saved screen state.
-
.screen_save ⇒ String
Save screen state.
-
.screen_scroll_down(lines = 1) ⇒ String
Scroll the screen down.
-
.screen_scroll_up(lines = 1) ⇒ String
Scroll the screen up.
-
.title(title) ⇒ String
Set the terminal window title.
-
.try_convert(attributes, separator: ' ') ⇒ String?
Try to convert a space-separated attribute string to an ANSI escape sequence.
-
.undecorate(str) ⇒ String
Remove all ANSI escape codes from a string.
BBcode Generation collapse
-
.bbcode(str) ⇒ String
Convert BBCode markup to ANSI escape codes.
-
.escape_bbcode(str) ⇒ String
Escape BBCode tags so they render as literal text after Ansi.bbcode processing.
-
.unbbcode(str) ⇒ String
Remove BBCode tags from a string, keeping the enclosed text.
Tool Functions collapse
-
.ansi?(str) ⇒ true, false
Test whether a string contains ANSI escape codes.
-
.plain(str) ⇒ String
Remove both BBCode tags and ANSI escape codes, returning plain text.
-
.valid?(*attributes) ⇒ true, false
Check whether all given attributes are valid.
Class Attribute Details
.attributes ⇒ Array<Symbol> (readonly)
All known text attribute names (bold, italic, underline, etc.).
27 |
# File 'lib/terminal/ansi.rb', line 27 def attributes = @attributes.dup |
.colors ⇒ Array<Symbol> (readonly)
All known basic color names.
33 |
# File 'lib/terminal/ansi.rb', line 33 def colors = @colors.dup |
.named_colors ⇒ Array<Symbol> (readonly)
All CSS/X11 named colors.
39 |
# File 'lib/terminal/ansi.rb', line 39 def named_colors = NAMED_COLORS.keys.map!(&:to_sym) |
Class Method Details
.[](*attributes) ⇒ String
Generate an ANSI escape sequence from attribute names or color indices.
Accepts symbols (:bold, :red), strings ("bold", "#ff0000"),
or integers for 256-color palette (0-255 foreground, 256-511
background, 512-767 underline color).
65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
# File 'lib/terminal/ansi.rb', line 65 def [](*attributes) return +'' if attributes.empty? "\e[#{ attributes .map! do |arg| case arg when String @attr_str_map[arg] || _invalid(arg) when Symbol @attr_sym_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 whether a string contains ANSI escape codes.
264 |
# File 'lib/terminal/ansi.rb', line 264 def ansi?(str) = @re_test.match?(str.to_s) |
.bbcode(str) ⇒ String
Convert BBCode markup to ANSI escape codes.
Tags like [bold] are converted to their ANSI equivalents;
[/bold] or [/] resets. Unknown tags are left unchanged.
Escape a tag with a backslash: [\\bold] renders as [bold].
191 192 193 194 195 196 197 198 199 200 |
# File 'lib/terminal/ansi.rb', line 191 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 try_convert(match) || match_str if match[0] != '\\' match[0] = '' "[#{match}]" end end |
.char_repeat(count = 1) ⇒ String
Repeat the last printed character.
452 |
# File 'lib/terminal/ansi.rb', line 452 def char_repeat(count = 1) = "\e[#{count}b" |
.cursor_back(columns = 1) ⇒ String
Move cursor left.
331 |
# File 'lib/terminal/ansi.rb', line 331 def cursor_back(columns = 1) = "\e[#{columns}D" |
.cursor_column(column = 1) ⇒ String
Move cursor to absolute column.
349 |
# File 'lib/terminal/ansi.rb', line 349 def cursor_column(column = 1) = "\e[#{column}G" |
.cursor_column_rel(column = 1) ⇒ String
Move cursor right by relative columns.
355 |
# File 'lib/terminal/ansi.rb', line 355 def cursor_column_rel(column = 1) = "\e[#{column}a" |
.cursor_down(lines = 1) ⇒ String
Move cursor down.
319 |
# File 'lib/terminal/ansi.rb', line 319 def cursor_down(lines = 1) = "\e[#{lines}B" |
.cursor_forward(columns = 1) ⇒ String
Move cursor right.
325 |
# File 'lib/terminal/ansi.rb', line 325 def cursor_forward(columns = 1) = "\e[#{columns}C" |
.cursor_hide ⇒ String
Hide cursor.
385 |
# File 'lib/terminal/ansi.rb', line 385 def cursor_hide = +CURSOR_HIDE |
.cursor_next_line(lines = 1) ⇒ String
Move cursor to beginning of line, N lines down.
337 |
# File 'lib/terminal/ansi.rb', line 337 def cursor_next_line(lines = 1) = "\e[#{lines}E" |
.cursor_pos(row, column = nil) ⇒ String
Move cursor to absolute position.
372 373 374 375 |
# File 'lib/terminal/ansi.rb', line 372 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 beginning of line, N lines up.
343 |
# File 'lib/terminal/ansi.rb', line 343 def cursor_prev_line(lines = 1) = "\e[#{lines}F" |
.cursor_restore_pos ⇒ String
Restore previously saved cursor position.
399 |
# File 'lib/terminal/ansi.rb', line 399 def cursor_restore_pos = +CURSOR_POS_RESTORE |
.cursor_row_rel(row = 1) ⇒ String
Move cursor down by relative rows.
361 |
# File 'lib/terminal/ansi.rb', line 361 def cursor_row_rel(row = 1) = "\e[#{row}e" |
.cursor_save_pos ⇒ String
Save current cursor position.
392 |
# File 'lib/terminal/ansi.rb', line 392 def cursor_save_pos = +CURSOR_POS_SAVE |
.cursor_show ⇒ String
Show cursor.
380 |
# File 'lib/terminal/ansi.rb', line 380 def cursor_show = +CURSOR_SHOW |
.cursor_up(lines = 1) ⇒ String
Move cursor up.
313 |
# File 'lib/terminal/ansi.rb', line 313 def cursor_up(lines = 1) = "\e[#{lines}A" |
.decorate(str, *attributes, reset: true) ⇒ String
Wrap a string with ANSI escape codes.
103 104 105 106 |
# File 'lib/terminal/ansi.rb', line 103 def decorate(str, *attributes, reset: true) attributes = self[*attributes] attributes.empty? ? "#{str}" : "#{attributes}#{str}#{"\e[m" if reset}" end |
.escape_bbcode(str) ⇒ String
Escape BBCode tags so they render as literal text after bbcode processing.
237 238 239 240 241 242 243 244 245 246 247 248 |
# File 'lib/terminal/ansi.rb', line 237 def escape_bbcode(str) str = str.to_s return str.dup unless str.index('[') str.gsub(@re_bbcode) do |match_str| fc = match_str[1] next match_str if fc == '\\' || fc == ']' match = Regexp.last_match(1) or next match_str next match_str if (parts = match.split).empty? next "[\\#{match}]" if parts.all? { @attr_str_map[it] } match_str end end |
.line_erase(part = :all) ⇒ String
Erase part of the current line.
459 |
# File 'lib/terminal/ansi.rb', line 459 def line_erase(part = :all) = "\e[#{@line_erase[part]}K" |
.link(url, text, **params) ⇒ String
Create a complete hyperlink (OSC 8).
504 505 506 |
# File 'lib/terminal/ansi.rb', line 504 def link(url, text, **params) "#{link_start(url, **params)}#{text}#{LINK_END}" end |
.link_end ⇒ String
End a hyperlink.
491 |
# File 'lib/terminal/ansi.rb', line 491 def link_end = +LINK_END |
.link_start(url, **params) ⇒ String
Supported by Ghostty, iTerm2, Kitty, Rio, Tabby, WezTerm.
Start a hyperlink (OSC 8).
482 483 484 |
# File 'lib/terminal/ansi.rb', line 482 def link_start(url, **params) "\e]8;#{params.map { it.join('=') }.join(':')};#{url}\a" end |
.notify(text) ⇒ String
Supported by Ghostty, iTerm2, Kitty, WezTerm.
Send a desktop notification via the terminal.
516 |
# File 'lib/terminal/ansi.rb', line 516 def notify(text) = "\e]9;#{text}\a" |
.plain(str) ⇒ String
Remove both BBCode tags and ANSI escape codes, returning plain text.
297 298 299 300 301 |
# File 'lib/terminal/ansi.rb', line 297 def plain(str) str = unbbcode(str) str.gsub!(@re_test, '') if str.index("\e") str end |
.progress(state, percent = 0) ⇒ String
Supported by Ghostty, iTerm2, Kitty.
Show or update a progress indicator in the terminal tab/title bar.
535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 |
# File 'lib/terminal/ansi.rb', line 535 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
Generate rainbow-colored text using true color (24-bit) ANSI codes.
154 155 156 157 158 159 160 161 162 163 164 165 166 |
# File 'lib/terminal/ansi.rb', line 154 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).round.abs};#{ (Math.sin(i + @pi2_third) * 255).round.abs };#{(Math.sin(i + @pi4_third) * 255).round.abs}m#{char}" end << RESET_FG ).join end |
.scale(text, scale: nil, width: nil, fracn: nil, fracd: nil, vertical: nil, horizontal: nil) ⇒ String
Only supported by Kitty.
Scale text using the text sizing protocol.
575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 |
# File 'lib/terminal/ansi.rb', line 575 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, :middle opts << 'v=2' end case horizontal when 0, :left opts << 'h=0' when 1, :right opts << 'h=1' when 2, :center opts << 'h=2' end end "\e]66;#{opts.join(':')};#{text}\a" end |
.screen_alternate ⇒ String
Switch to the alternate screen buffer.
427 |
# File 'lib/terminal/ansi.rb', line 427 def screen_alternate = +SCREEN_ALTERNATE |
.screen_alternate_off ⇒ String
Switch back from the alternate screen buffer.
434 |
# File 'lib/terminal/ansi.rb', line 434 def screen_alternate_off = +SCREEN_ALTERNATE_OFF |
.screen_erase(part = :all) ⇒ String
Erase part of the screen.
406 |
# File 'lib/terminal/ansi.rb', line 406 def screen_erase(part = :all) = "\e[#{@screen_erase[part]}J" |
.screen_restore ⇒ String
Restore previously saved screen state.
420 |
# File 'lib/terminal/ansi.rb', line 420 def screen_restore = +SCREEN_RESTORE |
.screen_save ⇒ String
Save screen state.
413 |
# File 'lib/terminal/ansi.rb', line 413 def screen_save = +SCREEN_SAVE |
.screen_scroll_down(lines = 1) ⇒ String
Scroll the screen down.
446 |
# File 'lib/terminal/ansi.rb', line 446 def screen_scroll_down(lines = 1) = "\e[#{lines}T" |
.screen_scroll_up(lines = 1) ⇒ String
Scroll the screen up.
440 |
# File 'lib/terminal/ansi.rb', line 440 def screen_scroll_up(lines = 1) = "\e[#{lines}S" |
.title(title) ⇒ String
Supported by Hyper, iTerm2, Kitty, macOS Terminal, Tabby, WezTerm.
Set the terminal window title.
470 |
# File 'lib/terminal/ansi.rb', line 470 def title(title) = "\e]0;#{title}\a" |
.try_convert(attributes, separator: ' ') ⇒ String?
Try to convert a space-separated attribute string to an ANSI escape sequence.
131 132 133 134 135 136 137 138 139 140 141 |
# File 'lib/terminal/ansi.rb', line 131 def try_convert(attributes, separator: ' ') return unless attributes cache = @convert_cache[separator] hit = cache[key = attributes.to_s] and return hit cache.clear if cache.size > 1024 # cache limit cache[key] = unless (attributes = key.split(separator)).empty? # attributes.uniq! codes = attributes.map { @attr_str_map[it] || break } "\e[#{codes.join(';')}m" if codes end end |
.unbbcode(str) ⇒ String
Remove BBCode tags from a string, keeping the enclosed text.
211 212 213 214 215 216 217 218 219 220 221 222 223 224 |
# File 'lib/terminal/ansi.rb', line 211 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 if match[0] == '\\' match[0] = '' next "[#{match}]" end next match_str if (match = match.split).empty? next if match.all? { @attr_str_map[it] } match_str end end |
.undecorate(str) ⇒ String
Remove all ANSI escape codes from a string.
116 117 118 |
# File 'lib/terminal/ansi.rb', line 116 def undecorate(str) (str = str.to_s).index("\e") ? str.gsub(@re_test, '') : str.dup end |
.valid?(*attributes) ⇒ true, false
Check whether all given attributes are valid.
275 276 277 278 279 280 281 282 283 284 285 286 |
# File 'lib/terminal/ansi.rb', line 275 def valid?(*attributes) attributes.all? do |arg| case arg when String @attr_str_map[arg] when Symbol @attr_sym_map[arg] when (0..767) true end end end |