1
# Copyright (c) 2001-2004 Twisted Matrix Laboratories.
2
# See LICENSE for details.
5
"""Module to parse ANSI escape sequences
7
Maintainer: Jean-Paul Calderone
13
from twisted.python import log
17
Represents an element of text along with the texts colors and
18
additional attributes.
22
COLORS = ('b', 'r', 'g', 'y', 'l', 'm', 'c', 'w')
23
BOLD_COLORS = tuple([x.upper() for x in COLORS])
24
BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = range(len(COLORS))
28
'Black', 'Red', 'Green', 'Yellow', 'Blue', 'Magenta', 'Cyan', 'White'
31
def __init__(self, text, fg, bg, display, bold, underline, flash, reverse):
32
self.text, self.fg, self.bg = text, fg, bg
33
self.display = display
35
self.underline = underline
37
self.reverse = reverse
39
self.fg, self.bg = self.bg, self.fg
44
Parser class for ANSI codes.
47
# Terminators for cursor movement ansi controls - unsupported
48
CURSOR_SET = ('H', 'f', 'A', 'B', 'C', 'D', 'R', 's', 'u', 'd','G')
50
# Terminators for erasure ansi controls - unsupported
51
ERASE_SET = ('J', 'K', 'P')
53
# Terminators for mode change ansi controls - unsupported
56
# Terminators for keyboard assignment ansi controls - unsupported
59
# Terminators for color change ansi controls - supported
62
SETS = (CURSOR_SET, ERASE_SET, MODE_SET, ASSIGN_SET, COLOR_SET)
64
def __init__(self, defaultFG, defaultBG):
65
self.defaultFG, self.defaultBG = defaultFG, defaultBG
66
self.currentFG, self.currentBG = self.defaultFG, self.defaultBG
67
self.bold, self.flash, self.underline, self.reverse = 0, 0, 0, 0
72
def stripEscapes(self, string):
74
Remove all ANSI color escapes from the given string.
81
if show == 0 and string[i] in _sets:
84
n = string.find('\x1B', i)
86
return result + string[i:]
88
result = result + string[i:n]
94
def writeString(self, colorstr):
97
def parseString(self, str):
99
Turn a string input into a list of L{ColorText} elements.
103
str = self.prepend + str
105
parts = str.split('\x1B')
108
self.writeString(self.formatText(parts[0]))
110
self.writeString(self.formatText(parts[0]))
116
if s[i] not in string.digits+'[;?':
120
self.prepend = '\x1b'
123
self.writeString(self.formatText(s[i+1:]))
129
self.prepend = '\x1b['
131
type = _setmap.get(s[i], None)
135
if type == AnsiParser.COLOR_SET:
136
self.parseColor(s[:i + 1])
138
self.writeString(self.formatText(s))
139
elif type == AnsiParser.CURSOR_SET:
140
cursor, s = s[:i+1], s[i+1:]
141
self.parseCursor(cursor)
142
self.writeString(self.formatText(s))
143
elif type == AnsiParser.ERASE_SET:
144
erase, s = s[:i+1], s[i+1:]
145
self.parseErase(erase)
146
self.writeString(self.formatText(s))
147
elif type == AnsiParser.MODE_SET:
148
mode, s = s[:i+1], s[i+1:]
149
#self.parseErase('2J')
150
self.writeString(self.formatText(s))
152
self.prepend = '\x1B[' + s
154
log.msg('Unhandled ANSI control type: %c' % (s[i],))
156
self.writeString(self.formatText(s))
158
def parseColor(self, str):
160
Handle a single ANSI color sequence
162
# Drop the trailing 'm'
169
parts = map(int, str.split(';'))
171
log.msg('Invalid ANSI color sequence (%d): %s' % (len(str), str))
172
self.currentFG, self.currentBG = self.defaultFG, self.defaultBG
177
self.currentFG, self.currentBG = self.defaultFG, self.defaultBG
178
self.bold, self.flash, self.underline, self.reverse = 0, 0, 0, 0
183
self.currentFG = x - 30
185
self.currentBG = x - 40
187
self.currentFG = self.defaultFG
189
self.currentBG = self.defaultBG
209
log.msg('Unrecognised ANSI color command: %d' % (x,))
211
def parseCursor(self, cursor):
214
def parseErase(self, erase):
218
def pickColor(self, value, mode, BOLD = ColorText.BOLD_COLORS):
220
return ColorText.COLORS[value]
222
return self.bold and BOLD[value] or ColorText.COLORS[value]
225
def formatText(self, text):
228
self.pickColor(self.currentFG, 0),
229
self.pickColor(self.currentBG, 1),
230
self.display, self.bold, self.underline, self.flash, self.reverse
234
_sets = ''.join(map(''.join, AnsiParser.SETS))
237
for s in AnsiParser.SETS: