OLD | NEW |
| (Empty) |
1 # -*- test-case-name: twisted.conch.test.test_text -*- | |
2 # Copyright (c) 2001-2004 Twisted Matrix Laboratories. | |
3 # See LICENSE for details. | |
4 | |
5 """ | |
6 Character attribute manipulation API | |
7 | |
8 This module provides a domain-specific language (using Python syntax) | |
9 for the creation of text with additional display attributes associated | |
10 with it. It is intended as an alternative to manually building up | |
11 strings containing ECMA 48 character attribute control codes. It | |
12 currently supports foreground and background colors (black, red, | |
13 green, yellow, blue, magenta, cyan, and white), intensity selection, | |
14 underlining, blinking and reverse video. Character set selection | |
15 support is planned. | |
16 | |
17 Character attributes are specified by using two Python operations: | |
18 attribute lookup and indexing. For example, the string \"Hello | |
19 world\" with red foreground and all other attributes set to their | |
20 defaults, assuming the name twisted.conch.insults.text.attributes has | |
21 been imported and bound to the name \"A\" (with the statement C{from | |
22 twisted.conch.insults.text import attributes as A}, for example) one | |
23 uses this expression:: | |
24 | |
25 | A.fg.red[\"Hello world\"] | |
26 | |
27 Other foreground colors are set by substituting their name for | |
28 \"red\". To set both a foreground and a background color, this | |
29 expression is used:: | |
30 | |
31 | A.fg.red[A.bg.green[\"Hello world\"]] | |
32 | |
33 Note that either A.bg.green can be nested within A.fg.red or vice | |
34 versa. Also note that multiple items can be nested within a single | |
35 index operation by separating them with commas:: | |
36 | |
37 | A.bg.green[A.fg.red[\"Hello\"], " ", A.fg.blue[\"world\"]] | |
38 | |
39 Other character attributes are set in a similar fashion. To specify a | |
40 blinking version of the previous expression:: | |
41 | |
42 | A.blink[A.bg.green[A.fg.red[\"Hello\"], " ", A.fg.blue[\"world\"]]] | |
43 | |
44 C{A.reverseVideo}, C{A.underline}, and C{A.bold} are also valid. | |
45 | |
46 A third operation is actually supported: unary negation. This turns | |
47 off an attribute when an enclosing expression would otherwise have | |
48 caused it to be on. For example:: | |
49 | |
50 | A.underline[A.fg.red[\"Hello\", -A.underline[\" world\"]]] | |
51 | |
52 @author: U{Jp Calderone<mailto:exarkun@twistedmatrix.com>} | |
53 """ | |
54 | |
55 from twisted.conch.insults import helper, insults | |
56 | |
57 class _Attribute(object): | |
58 def __init__(self): | |
59 self.children = [] | |
60 | |
61 def __getitem__(self, item): | |
62 assert isinstance(item, (list, tuple, _Attribute, str)) | |
63 if isinstance(item, (list, tuple)): | |
64 self.children.extend(item) | |
65 else: | |
66 self.children.append(item) | |
67 return self | |
68 | |
69 def serialize(self, write, attrs=None): | |
70 if attrs is None: | |
71 attrs = helper.CharacterAttribute() | |
72 for ch in self.children: | |
73 if isinstance(ch, _Attribute): | |
74 ch.serialize(write, attrs.copy()) | |
75 else: | |
76 write(attrs.toVT102()) | |
77 write(ch) | |
78 | |
79 class _NormalAttr(_Attribute): | |
80 def serialize(self, write, attrs): | |
81 attrs.__init__() | |
82 super(_NormalAttr, self).serialize(write, attrs) | |
83 | |
84 class _OtherAttr(_Attribute): | |
85 def __init__(self, attrname, attrvalue): | |
86 self.attrname = attrname | |
87 self.attrvalue = attrvalue | |
88 self.children = [] | |
89 | |
90 def __neg__(self): | |
91 result = _OtherAttr(self.attrname, not self.attrvalue) | |
92 result.children.extend(self.children) | |
93 return result | |
94 | |
95 def serialize(self, write, attrs): | |
96 attrs = attrs.wantOne(**{self.attrname: self.attrvalue}) | |
97 super(_OtherAttr, self).serialize(write, attrs) | |
98 | |
99 class _ColorAttr(_Attribute): | |
100 def __init__(self, color, ground): | |
101 self.color = color | |
102 self.ground = ground | |
103 self.children = [] | |
104 | |
105 def serialize(self, write, attrs): | |
106 attrs = attrs.wantOne(**{self.ground: self.color}) | |
107 super(_ColorAttr, self).serialize(write, attrs) | |
108 | |
109 class _ForegroundColorAttr(_ColorAttr): | |
110 def __init__(self, color): | |
111 super(_ForegroundColorAttr, self).__init__(color, 'foreground') | |
112 | |
113 class _BackgroundColorAttr(_ColorAttr): | |
114 def __init__(self, color): | |
115 super(_BackgroundColorAttr, self).__init__(color, 'background') | |
116 | |
117 class CharacterAttributes(object): | |
118 class _ColorAttribute(object): | |
119 def __init__(self, ground): | |
120 self.ground = ground | |
121 | |
122 attrs = { | |
123 'black': helper.BLACK, | |
124 'red': helper.RED, | |
125 'green': helper.GREEN, | |
126 'yellow': helper.YELLOW, | |
127 'blue': helper.BLUE, | |
128 'magenta': helper.MAGENTA, | |
129 'cyan': helper.CYAN, | |
130 'white': helper.WHITE} | |
131 | |
132 def __getattr__(self, name): | |
133 try: | |
134 return self.ground(self.attrs[name]) | |
135 except KeyError: | |
136 raise AttributeError(name) | |
137 | |
138 fg = _ColorAttribute(_ForegroundColorAttr) | |
139 bg = _ColorAttribute(_BackgroundColorAttr) | |
140 | |
141 attrs = { | |
142 'bold': insults.BOLD, | |
143 'blink': insults.BLINK, | |
144 'underline': insults.UNDERLINE, | |
145 'reverseVideo': insults.REVERSE_VIDEO} | |
146 | |
147 def __getattr__(self, name): | |
148 if name == 'normal': | |
149 return _NormalAttr() | |
150 if name in self.attrs: | |
151 return _OtherAttr(name, True) | |
152 raise AttributeError(name) | |
153 | |
154 def flatten(output, attrs): | |
155 """Serialize a sequence of characters with attribute information | |
156 | |
157 The resulting string can be interpreted by VT102-compatible | |
158 terminals so that the contained characters are displayed and, for | |
159 those attributes which the terminal supports, have the attributes | |
160 specified in the input. | |
161 | |
162 For example, if your terminal is VT102 compatible, you might run | |
163 this for a colorful variation on the \"hello world\" theme:: | |
164 | |
165 | from twisted.conch.insults.text import flatten, attributes as A | |
166 | from twisted.conch.insults.helper import CharacterAttribute | |
167 | print flatten( | |
168 | A.normal[A.bold[A.fg.red['He'], A.fg.green['ll'], A.fg.magenta['o'],
' ', | |
169 | A.fg.yellow['Wo'], A.fg.blue['rl'], A.fg.cyan['d!']]]
, | |
170 | CharacterAttribute()) | |
171 | |
172 @param output: Object returned by accessing attributes of the | |
173 module-level attributes object. | |
174 | |
175 @param attrs: A L{twisted.conch.insults.helper.CharacterAttribute} | |
176 instance | |
177 | |
178 @return: A VT102-friendly string | |
179 """ | |
180 L = [] | |
181 output.serialize(L.append, attrs) | |
182 return ''.join(L) | |
183 | |
184 attributes = CharacterAttributes() | |
185 | |
186 __all__ = ['attributes', 'flatten'] | |
OLD | NEW |