~0x44/nova/extdoc

« back to all changes in this revision

Viewing changes to vendor/Twisted-10.0.0/twisted/conch/ui/ansi.py

  • Committer: Jesse Andrews
  • Date: 2010-05-28 06:05:26 UTC
  • Revision ID: git-v1:bf6e6e718cdc7488e2da87b21e258ccc065fe499
initial commit

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (c) 2001-2004 Twisted Matrix Laboratories.
 
2
# See LICENSE for details.
 
3
 
 
4
#
 
5
"""Module to parse ANSI escape sequences
 
6
 
 
7
Maintainer: Jean-Paul Calderone
 
8
"""
 
9
 
 
10
import string
 
11
 
 
12
# Twisted imports
 
13
from twisted.python import log
 
14
 
 
15
class ColorText:
 
16
    """
 
17
    Represents an element of text along with the texts colors and
 
18
    additional attributes.
 
19
    """
 
20
 
 
21
    # The colors to use
 
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))
 
25
 
 
26
    # Color names
 
27
    COLOR_NAMES = (
 
28
        'Black', 'Red', 'Green', 'Yellow', 'Blue', 'Magenta', 'Cyan', 'White'
 
29
    )
 
30
 
 
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
 
34
        self.bold = bold
 
35
        self.underline = underline
 
36
        self.flash = flash
 
37
        self.reverse = reverse
 
38
        if self.reverse:
 
39
            self.fg, self.bg = self.bg, self.fg
 
40
 
 
41
 
 
42
class AnsiParser:
 
43
    """
 
44
    Parser class for ANSI codes.
 
45
    """
 
46
 
 
47
    # Terminators for cursor movement ansi controls - unsupported
 
48
    CURSOR_SET = ('H', 'f', 'A', 'B', 'C', 'D', 'R', 's', 'u', 'd','G')
 
49
 
 
50
    # Terminators for erasure ansi controls - unsupported
 
51
    ERASE_SET = ('J', 'K', 'P')
 
52
    
 
53
    # Terminators for mode change ansi controls - unsupported
 
54
    MODE_SET = ('h', 'l')
 
55
    
 
56
    # Terminators for keyboard assignment ansi controls - unsupported
 
57
    ASSIGN_SET = ('p',)
 
58
    
 
59
    # Terminators for color change ansi controls - supported
 
60
    COLOR_SET = ('m',)
 
61
 
 
62
    SETS = (CURSOR_SET, ERASE_SET, MODE_SET, ASSIGN_SET, COLOR_SET)
 
63
 
 
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
 
68
        self.display = 1
 
69
        self.prepend = ''
 
70
 
 
71
    
 
72
    def stripEscapes(self, string):
 
73
        """
 
74
        Remove all ANSI color escapes from the given string.
 
75
        """
 
76
        result = ''
 
77
        show = 1
 
78
        i = 0
 
79
        L = len(string)
 
80
        while i < L:
 
81
            if show == 0 and string[i] in _sets:
 
82
                show = 1
 
83
            elif show:
 
84
                n = string.find('\x1B', i)
 
85
                if n == -1:
 
86
                    return result + string[i:]
 
87
                else:
 
88
                    result = result + string[i:n]
 
89
                    i = n
 
90
                    show = 0
 
91
            i = i + 1
 
92
        return result
 
93
 
 
94
    def writeString(self, colorstr):
 
95
        pass
 
96
 
 
97
    def parseString(self, str):
 
98
        """
 
99
        Turn a string input into a list of L{ColorText} elements.
 
100
        """
 
101
 
 
102
        if self.prepend:
 
103
            str = self.prepend + str
 
104
            self.prepend = ''
 
105
        parts = str.split('\x1B')
 
106
        
 
107
        if len(parts) == 1:
 
108
            self.writeString(self.formatText(parts[0]))
 
109
        else:
 
110
            self.writeString(self.formatText(parts[0]))
 
111
            for s in parts[1:]:
 
112
                L = len(s)
 
113
                i = 0 
 
114
                type = None
 
115
                while i < L:
 
116
                    if s[i] not in string.digits+'[;?':
 
117
                        break
 
118
                    i+=1
 
119
                if not s:
 
120
                    self.prepend = '\x1b'
 
121
                    return
 
122
                if s[0]!='[':
 
123
                    self.writeString(self.formatText(s[i+1:]))
 
124
                    continue
 
125
                else:
 
126
                    s=s[1:]
 
127
                    i-=1
 
128
                if i==L-1:
 
129
                    self.prepend = '\x1b['
 
130
                    return
 
131
                type = _setmap.get(s[i], None)
 
132
                if type is None:
 
133
                    continue 
 
134
 
 
135
                if type == AnsiParser.COLOR_SET:
 
136
                    self.parseColor(s[:i + 1])
 
137
                    s = 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))
 
151
                elif i == L:
 
152
                    self.prepend = '\x1B[' + s
 
153
                else:
 
154
                    log.msg('Unhandled ANSI control type: %c' % (s[i],))
 
155
                    s = s[i + 1:]
 
156
                    self.writeString(self.formatText(s))
 
157
 
 
158
    def parseColor(self, str):
 
159
        """
 
160
        Handle a single ANSI color sequence
 
161
        """
 
162
        # Drop the trailing 'm'
 
163
        str = str[:-1]
 
164
 
 
165
        if not str:
 
166
            str = '0'
 
167
 
 
168
        try:
 
169
            parts = map(int, str.split(';'))
 
170
        except ValueError:
 
171
            log.msg('Invalid ANSI color sequence (%d): %s' % (len(str), str))
 
172
            self.currentFG, self.currentBG = self.defaultFG, self.defaultBG
 
173
            return
 
174
 
 
175
        for x in parts:
 
176
            if x == 0:
 
177
                self.currentFG, self.currentBG = self.defaultFG, self.defaultBG
 
178
                self.bold, self.flash, self.underline, self.reverse = 0, 0, 0, 0
 
179
                self.display = 1
 
180
            elif x == 1:
 
181
                self.bold = 1
 
182
            elif 30 <= x <= 37:
 
183
                self.currentFG = x - 30
 
184
            elif 40 <= x <= 47:
 
185
                self.currentBG = x - 40
 
186
            elif x == 39:
 
187
                self.currentFG = self.defaultFG
 
188
            elif x == 49:
 
189
                self.currentBG = self.defaultBG
 
190
            elif x == 4:
 
191
                self.underline = 1
 
192
            elif x == 5:
 
193
                self.flash = 1
 
194
            elif x == 7:
 
195
                self.reverse = 1
 
196
            elif x == 8:
 
197
                self.display = 0
 
198
            elif x == 22:
 
199
                self.bold = 0
 
200
            elif x == 24:
 
201
                self.underline = 0
 
202
            elif x == 25:
 
203
                self.blink = 0
 
204
            elif x == 27:
 
205
                self.reverse = 0
 
206
            elif x == 28:
 
207
                self.display = 1
 
208
            else:
 
209
                log.msg('Unrecognised ANSI color command: %d' % (x,))
 
210
 
 
211
    def parseCursor(self, cursor):
 
212
        pass
 
213
 
 
214
    def parseErase(self, erase):
 
215
        pass
 
216
 
 
217
 
 
218
    def pickColor(self, value, mode, BOLD = ColorText.BOLD_COLORS):
 
219
        if mode:
 
220
            return ColorText.COLORS[value]
 
221
        else:
 
222
            return self.bold and BOLD[value] or ColorText.COLORS[value]
 
223
 
 
224
 
 
225
    def formatText(self, text):
 
226
        return ColorText(
 
227
            text,
 
228
            self.pickColor(self.currentFG, 0),
 
229
            self.pickColor(self.currentBG, 1),
 
230
            self.display, self.bold, self.underline, self.flash, self.reverse
 
231
        )
 
232
 
 
233
 
 
234
_sets = ''.join(map(''.join, AnsiParser.SETS))
 
235
 
 
236
_setmap = {}
 
237
for s in AnsiParser.SETS:
 
238
    for r in s:
 
239
        _setmap[r] = s
 
240
del s