Module: Chamomile::ANSI

Defined in:
lib/chamomile/styling/ansi.rb

Constant Summary collapse

ESCAPE_RE =

Matches CSI sequences, OSC sequences, ESC charset/other sequences

/\e\[[0-9;]*[A-Za-z]|\e\][^\a\e]*(?:\a|\e\\)|\e[()][AB012]|\e./

Class Method Summary collapse

Class Method Details

.extract_escape(chars, start) ⇒ Object



34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/chamomile/styling/ansi.rb', line 34

def extract_escape(chars, start)
  return nil unless chars[start] == "\e"

  i = start + 1
  return nil if i >= chars.length

  if chars[i] == "["
    seq = +"\e["
    i += 1
    while i < chars.length && chars[i].match?(/[0-9;]/)
      seq << chars[i]
      i += 1
    end
    if i < chars.length && chars[i].match?(/[A-Za-z]/)
      seq << chars[i]
      return seq
    end
  end

  nil
end

.height(str) ⇒ Object



22
23
24
# File 'lib/chamomile/styling/ansi.rb', line 22

def height(str)
  str.count("\n") + 1
end

.printable_width(str) ⇒ Object



13
14
15
16
17
18
19
20
# File 'lib/chamomile/styling/ansi.rb', line 13

def printable_width(str)
  stripped = strip(str)
  width = 0
  stripped.each_char do |ch|
    width += char_width(ch)
  end
  width
end

.sgr_open_after?(was_open, seq) ⇒ Boolean

Returns:

  • (Boolean)


68
69
70
71
72
73
# File 'lib/chamomile/styling/ansi.rb', line 68

def sgr_open_after?(was_open, seq)
  return false if ["\e[0m", "\e[m"].include?(seq)
  return true if seq.match?(/\A\e\[\d/)

  was_open
end

.size(str) ⇒ Object



26
27
28
29
30
31
32
# File 'lib/chamomile/styling/ansi.rb', line 26

def size(str)
  return [0, 1] if str.empty?

  lines = str.split("\n", -1)
  w = lines.map { |line| printable_width(line) }.max || 0
  [w, lines.length]
end

.strip(str) ⇒ Object



9
10
11
# File 'lib/chamomile/styling/ansi.rb', line 9

def strip(str)
  str.gsub(ESCAPE_RE, "")
end

.track_sgr(active_sgr, seq) ⇒ Object



56
57
58
59
60
61
62
63
64
65
66
# File 'lib/chamomile/styling/ansi.rb', line 56

def track_sgr(active_sgr, seq)
  if ["\e[0m", "\e[m"].include?(seq)
    active_sgr.clear
  elsif seq.match?(/\A\e\[\d/)
    if active_sgr.empty?
      active_sgr.replace(seq)
    else
      active_sgr.replace("#{active_sgr.delete_suffix("\e[0m")}#{seq}")
    end
  end
end