def self.parse(text)
scanner = StringScanner.new(text)
state = {}
result = []
ansi_stack = []
text_chunk = ''
until scanner.eos?
char = scanner.getch
if char == ESC && (delim = scanner.getch) == CSI
if scanner.scan(/^0m/)
unpack_ansi(ansi_stack) { |attr, name| state[attr] = name }
ansi_stack = []
elsif scanner.scan(/^([1-9;:]+)m/)
if !text_chunk.empty? && !ansi_stack.empty?
unpack_ansi(ansi_stack) { |attr, name| state[attr] = name }
ansi_stack = []
end
scanner[1].split(/:|;/).each do |code|
ansi_stack << code
end
end
if !text_chunk.empty?
state[:text] = text_chunk
result.push(state)
state = {}
text_chunk = ''
end
elsif char == ESC
text_chunk << char + delim.to_s
else
text_chunk << char
end
end
if !text_chunk.empty?
state[:text] = text_chunk
end
if !ansi_stack.empty?
unpack_ansi(ansi_stack) { |attr, name| state[attr] = name}
end
if state.values.any? { |val| !val.empty? }
result.push(state)
end
result
end