root/trunk/twisted/conch/insults/text.py

Revision 30752, 6.2 KB (checked in by exarkun, 15 months ago)

Rewrite the copyright headers to exclude date information.

Author: exarkun
Reviewer: glyph
Fixes: #4857

To avoid the need to perpetually update copyright dates in each file in Twisted,
remove the dates from most files and just leave them in the LICENSE file.

As a side effect, some files also have had a trailing newline added where it was
missing before.

Line 
1# -*- test-case-name: twisted.conch.test.test_text -*-
2# Copyright (c) Twisted Matrix Laboratories.
3# See LICENSE for details.
4
5"""
6Character attribute manipulation API
7
8This module provides a domain-specific language (using Python syntax)
9for the creation of text with additional display attributes associated
10with it.  It is intended as an alternative to manually building up
11strings containing ECMA 48 character attribute control codes.  It
12currently supports foreground and background colors (black, red,
13green, yellow, blue, magenta, cyan, and white), intensity selection,
14underlining, blinking and reverse video.  Character set selection
15support is planned.
16
17Character attributes are specified by using two Python operations:
18attribute lookup and indexing.  For example, the string \"Hello
19world\" with red foreground and all other attributes set to their
20defaults, assuming the name twisted.conch.insults.text.attributes has
21been imported and bound to the name \"A\" (with the statement C{from
22twisted.conch.insults.text import attributes as A}, for example) one
23uses this expression::
24
25 | A.fg.red[\"Hello world\"]
26
27Other foreground colors are set by substituting their name for
28\"red\".  To set both a foreground and a background color, this
29expression is used::
30
31 | A.fg.red[A.bg.green[\"Hello world\"]]
32
33Note that either A.bg.green can be nested within A.fg.red or vice
34versa.  Also note that multiple items can be nested within a single
35index operation by separating them with commas::
36
37 | A.bg.green[A.fg.red[\"Hello\"], " ", A.fg.blue[\"world\"]]
38
39Other character attributes are set in a similar fashion.  To specify a
40blinking version of the previous expression::
41
42 | A.blink[A.bg.green[A.fg.red[\"Hello\"], " ", A.fg.blue[\"world\"]]]
43
44C{A.reverseVideo}, C{A.underline}, and C{A.bold} are also valid.
45
46A third operation is actually supported: unary negation.  This turns
47off an attribute when an enclosing expression would otherwise have
48caused it to be on.  For example::
49
50 | A.underline[A.fg.red[\"Hello\", -A.underline[\" world\"]]]
51
52@author: Jp Calderone
53"""
54
55from twisted.conch.insults import helper, insults
56
57class _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
79class _NormalAttr(_Attribute):
80    def serialize(self, write, attrs):
81        attrs.__init__()
82        super(_NormalAttr, self).serialize(write, attrs)
83
84class _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
99class _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
109class _ForegroundColorAttr(_ColorAttr):
110    def __init__(self, color):
111        super(_ForegroundColorAttr, self).__init__(color, 'foreground')
112
113class _BackgroundColorAttr(_ColorAttr):
114    def __init__(self, color):
115        super(_BackgroundColorAttr, self).__init__(color, 'background')
116
117class 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
154def 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
184attributes = CharacterAttributes()
185
186__all__ = ['attributes', 'flatten']
Note: See TracBrowser for help on using the browser.