Add initial project files

This commit is contained in:
Kim Silkebækken 2012-11-08 12:23:57 +01:00
parent adb67e1e1f
commit cf88b086c0
7 changed files with 364 additions and 0 deletions

0
README.rst Normal file
View File

0
lib/__init__.py Normal file
View File

54
lib/colors.py Normal file
View File

@ -0,0 +1,54 @@
def cterm_to_hex(cterm_color):
'''Translate a cterm color index into the corresponding hex/RGB color.
'''
color_dict = {
16: 0x000000, 17: 0x00005f, 18: 0x000087, 19: 0x0000af, 20: 0x0000d7, 21: 0x0000ff,
22: 0x005f00, 23: 0x005f5f, 24: 0x005f87, 25: 0x005faf, 26: 0x005fd7, 27: 0x005fff,
28: 0x008700, 29: 0x00875f, 30: 0x008787, 31: 0x0087af, 32: 0x0087d7, 33: 0x0087ff,
34: 0x00af00, 35: 0x00af5f, 36: 0x00af87, 37: 0x00afaf, 38: 0x00afd7, 39: 0x00afff,
40: 0x00d700, 41: 0x00d75f, 42: 0x00d787, 43: 0x00d7af, 44: 0x00d7d7, 45: 0x00d7ff,
46: 0x00ff00, 47: 0x00ff5f, 48: 0x00ff87, 49: 0x00ffaf, 50: 0x00ffd7, 51: 0x00ffff,
52: 0x5f0000, 53: 0x5f005f, 54: 0x5f0087, 55: 0x5f00af, 56: 0x5f00d7, 57: 0x5f00ff,
58: 0x5f5f00, 59: 0x5f5f5f, 60: 0x5f5f87, 61: 0x5f5faf, 62: 0x5f5fd7, 63: 0x5f5fff,
64: 0x5f8700, 65: 0x5f875f, 66: 0x5f8787, 67: 0x5f87af, 68: 0x5f87d7, 69: 0x5f87ff,
70: 0x5faf00, 71: 0x5faf5f, 72: 0x5faf87, 73: 0x5fafaf, 74: 0x5fafd7, 75: 0x5fafff,
76: 0x5fd700, 77: 0x5fd75f, 78: 0x5fd787, 79: 0x5fd7af, 80: 0x5fd7d7, 81: 0x5fd7ff,
82: 0x5fff00, 83: 0x5fff5f, 84: 0x5fff87, 85: 0x5fffaf, 86: 0x5fffd7, 87: 0x5fffff,
88: 0x870000, 89: 0x87005f, 90: 0x870087, 91: 0x8700af, 92: 0x8700d7, 93: 0x8700ff,
94: 0x875f00, 95: 0x875f5f, 96: 0x875f87, 97: 0x875faf, 98: 0x875fd7, 99: 0x875fff,
100: 0x878700, 101: 0x87875f, 102: 0x878787, 103: 0x8787af, 104: 0x8787d7, 105: 0x8787ff,
106: 0x87af00, 107: 0x87af5f, 108: 0x87af87, 109: 0x87afaf, 110: 0x87afd7, 111: 0x87afff,
112: 0x87d700, 113: 0x87d75f, 114: 0x87d787, 115: 0x87d7af, 116: 0x87d7d7, 117: 0x87d7ff,
118: 0x87ff00, 119: 0x87ff5f, 120: 0x87ff87, 121: 0x87ffaf, 122: 0x87ffd7, 123: 0x87ffff,
124: 0xaf0000, 125: 0xaf005f, 126: 0xaf0087, 127: 0xaf00af, 128: 0xaf00d7, 129: 0xaf00ff,
130: 0xaf5f00, 131: 0xaf5f5f, 132: 0xaf5f87, 133: 0xaf5faf, 134: 0xaf5fd7, 135: 0xaf5fff,
136: 0xaf8700, 137: 0xaf875f, 138: 0xaf8787, 139: 0xaf87af, 140: 0xaf87d7, 141: 0xaf87ff,
142: 0xafaf00, 143: 0xafaf5f, 144: 0xafaf87, 145: 0xafafaf, 146: 0xafafd7, 147: 0xafafff,
148: 0xafd700, 149: 0xafd75f, 150: 0xafd787, 151: 0xafd7af, 152: 0xafd7d7, 153: 0xafd7ff,
154: 0xafff00, 155: 0xafff5f, 156: 0xafff87, 157: 0xafffaf, 158: 0xafffd7, 159: 0xafffff,
160: 0xd70000, 161: 0xd7005f, 162: 0xd70087, 163: 0xd700af, 164: 0xd700d7, 165: 0xd700ff,
166: 0xd75f00, 167: 0xd75f5f, 168: 0xd75f87, 169: 0xd75faf, 170: 0xd75fd7, 171: 0xd75fff,
172: 0xd78700, 173: 0xd7875f, 174: 0xd78787, 175: 0xd787af, 176: 0xd787d7, 177: 0xd787ff,
178: 0xd7af00, 179: 0xd7af5f, 180: 0xd7af87, 181: 0xd7afaf, 182: 0xd7afd7, 183: 0xd7afff,
184: 0xd7d700, 185: 0xd7d75f, 186: 0xd7d787, 187: 0xd7d7af, 188: 0xd7d7d7, 189: 0xd7d7ff,
190: 0xd7ff00, 191: 0xd7ff5f, 192: 0xd7ff87, 193: 0xd7ffaf, 194: 0xd7ffd7, 195: 0xd7ffff,
196: 0xff0000, 197: 0xff005f, 198: 0xff0087, 199: 0xff00af, 200: 0xff00d7, 201: 0xff00ff,
202: 0xff5f00, 203: 0xff5f5f, 204: 0xff5f87, 205: 0xff5faf, 206: 0xff5fd7, 207: 0xff5fff,
208: 0xff8700, 209: 0xff875f, 210: 0xff8787, 211: 0xff87af, 212: 0xff87d7, 213: 0xff87ff,
214: 0xffaf00, 215: 0xffaf5f, 216: 0xffaf87, 217: 0xffafaf, 218: 0xffafd7, 219: 0xffafff,
220: 0xffd700, 221: 0xffd75f, 222: 0xffd787, 223: 0xffd7af, 224: 0xffd7d7, 225: 0xffd7ff,
226: 0xffff00, 227: 0xffff5f, 228: 0xffff87, 229: 0xffffaf, 230: 0xffffd7, 231: 0xffffff,
232: 0x080808, 233: 0x121212, 234: 0x1c1c1c, 235: 0x262626, 236: 0x303030, 237: 0x3a3a3a,
238: 0x444444, 239: 0x4e4e4e, 240: 0x585858, 241: 0x626262, 242: 0x6c6c6c, 243: 0x767676,
244: 0x808080, 245: 0x8a8a8a, 246: 0x949494, 247: 0x9e9e9e, 248: 0xa8a8a8, 249: 0xb2b2b2,
250: 0xbcbcbc, 251: 0xc6c6c6, 252: 0xd0d0d0, 253: 0xdadada, 254: 0xe4e4e4, 255: 0xeeeeee,
}
if not cterm_color:
return None
try:
return color_dict[cterm_color]
except KeyError:
import sys
sys.stderr.write('Invalid cterm color index: {0}\n'.format(cterm_color))
return None

236
lib/core.py Normal file
View File

@ -0,0 +1,236 @@
class Segment:
'''Powerline segment renderer.
Powerline segments are initially structured as a tree of segments and sub
segments. This is to give the segments a sense of grouping and "scope", to
avoid having to define all the properties (fg, bg, etc.) for every single
segment. By grouping you can e.g. provide a common background color for
several segments.
Usage example:
from lib.core import Segment
from lib.renderers import TerminalSegmentRenderer
powerline = Segment([
Segment('First segment'),
Segment([
Segment('Grouped segment 1'),
Segment('Grouped segment 2'),
]),
])
print(powerline.render(TerminalSegmentRenderer))
'''
separators = {
'l': {
'hard': '',
'soft': '',
},
'r': {
'hard': '',
'soft': '',
},
}
ATTR_BOLD = 1
ATTR_ITALIC = 2
ATTR_UNDERLINE = 4
def __init__(self, content='', fg=None, bg=None, attr=None, side=None, padding=None, separate=None):
'''Create a new segment.
No arguments are required when creating new segments, as
empty/colorless segments can be used e.g. as left/right separators.
'''
self.content = content
self.parent = None
self.prev = None
self.next = None
try:
if len(fg) == 2:
self._fg = fg
except TypeError:
# Only the terminal color is defined, so we need to get the hex color
from lib.colors import cterm_to_hex
self._fg = [fg, cterm_to_hex(fg)]
try:
if len(bg) == 2:
self._bg = bg
except TypeError:
# Only the terminal color is defined, so we need to get the hex color
from lib.colors import cterm_to_hex
self._bg = [bg, cterm_to_hex(bg)]
self._attr = attr
self._side = side
self._padding = padding
self._separate = separate
@property
def fg(self):
'''Segment foreground color property.
Recursively searches for the property or the parent segments' property
until one is found. If this property is not defined anywhere in the
tree, the default foreground color is used.
'''
if self.parent and self._fg[0] is None:
return self.parent.fg
return self._fg if self._fg[0] is not None else [None, None]
@property
def bg(self):
'''Segment background color property.
Recursively searches for the property or the parent segments' property
until one is found. If this property is not defined anywhere in the
tree, the default background color is used.
'''
if self.parent and self._bg[0] is None:
return self.parent.bg
return self._bg if self._bg[0] is not None else [None, None]
@property
def attr(self):
'''Segment attribute property.
Recursively searches for the property or the parent segments' property
until one is found. If this property is not defined anywhere in the
tree, no attributes are applied.
'''
if self.parent and self._attr is None:
return self.parent.attr
return self._attr if self._attr is not None else 0
@property
def side(self):
'''Segment side property.
Recursively searches for the property or the parent segments' property
until one is found. If this property is not defined anywhere in the
tree, the left side is used for all segments.
'''
if self.parent and self._side is None:
return self.parent.side
return self._side if self._side is not None else 'l'
@property
def padding(self):
'''Segment padding property.
Return which string is used to pad the segment before and after the
separator symbol.
Recursively searches for the property or the parent segments' property
until one is found. If this property is not defined anywhere in the
tree, a single space is used for padding.
'''
if self.parent and self._padding is None:
return self.parent.padding
return self._padding if self._padding is not None else ' '
@property
def separate(self):
'''Segment separation property.
Returns whether a separator symbol should be drawn before/after the
segment.
Recursively searches for the property or the parent segments' property
until one is found. If this property is not defined anywhere in the
tree, then separators will be drawn around all segments.
'''
if self.parent and self._separate is None:
return self.parent.separate
return self._separate if self._separate is not None else True
@property
def separator(self):
'''Segment separator property.
Returns the separator symbol to be used, depending on which side this
segment is on.
'''
return self.separators[self.side]
def render(self, renderer):
'''Render the segment and all child segments.
This method flattens the segment and all its child segments into
a one-dimensional array. It then loops through this array and compares
the foreground/background colors and separator/padding properties and
returns the rendered statusline as a string.
'''
def flatten(segment):
'''Flattens the segment tree into a one-dimensional array.
'''
ret = []
for child_segment in segment.content:
child_segment.parent = segment
if isinstance(child_segment.content, str):
# If the contents of the child segment is a string then
# this is a tree node
ret.append(child_segment)
else:
# This is a segment group that should be flattened
ret += flatten(child_segment)
return ret
segments = flatten(self)
output = ''
# Loop through the segment array and create the segments, colors and
# separators
#
# TODO Make this prettier
for idx, segment in enumerate(segments):
# Ensure that we always have a previous/next segment, if we're at
# the beginning/end of the array an empty segment is used for the
# prev/next segment
segment.prev = segments[idx - 1] if idx > 0 else Segment()
segment.next = segments[idx + 1] if idx < len(segments) - 1 else Segment()
if segment.side == 'l':
output += renderer.fg(segment.fg[0])
output += renderer.bg(segment.bg[0])
output += segment.padding
output += renderer.attr(segment.attr)
output += segment.content
output += renderer.attr(None)
if segment.content:
if segment.next.bg == segment.bg:
if segment.next.content and segment.separate:
output += segment.padding
output += segment.separator['soft']
# Don't draw a hard separator if the next segment is on
# the opposite side, it screws up the coloring
elif segment.next.side == segment.side:
output += segment.padding
output += renderer.fg(segment.bg[0])
output += renderer.bg(segment.next.bg[0])
output += segment.separator['hard']
else:
pad_pre = False
if segment.content:
if segment.prev.bg == segment.bg:
if segment.prev.content and segment.separate:
pad_pre = True
output += segment.separator['soft']
else:
pad_pre = True
output += renderer.fg(segment.bg[0])
output += renderer.bg(segment.prev.bg[0])
output += segment.separator['hard']
output += renderer.fg(segment.fg[0])
output += renderer.bg(segment.bg[0])
if pad_pre:
output += segment.padding
output += renderer.attr(segment.attr)
output += segment.content
output += renderer.attr(None)
output += segment.padding
return output

View File

@ -0,0 +1,8 @@
class SegmentRenderer:
def fg(col):
raise NotImplementedError
def bg(col):
raise NotImplementedError
from lib.renderers.terminal import TerminalSegmentRenderer

48
lib/renderers/terminal.py Normal file
View File

@ -0,0 +1,48 @@
#!/usr/bin/env python
from lib.core import Segment
from lib.renderers import SegmentRenderer
class TerminalSegmentRenderer(SegmentRenderer):
'''Powerline terminal segment renderer.
'''
def fg(col):
'''Return ANSI escape code for foreground colors.
If no color is provided, the color is reset to the terminal default.
'''
if col:
return '[38;5;{0}m'.format(col)
else:
return ''
def bg(col):
'''Return ANSI escape code for background colors.
If no color is provided, the color is reset to the terminal default.
'''
if col:
return '[48;5;{0}m'.format(col)
else:
return ''
def attr(attrs):
'''Return ANSI escape code for attributes.
Accepts a flag with attributes defined in Segment.
If no attributes are provided, the attributes are reset to the terminal
defaults.
'''
if not attrs:
return ''
ansi_attrs = []
if attrs & Segment.ATTR_BOLD:
ansi_attrs.append('1')
if ansi_attrs:
return '[{0}m'.format(';'.join(ansi_attrs))
return ''

18
powerline-terminal-example.py Executable file
View File

@ -0,0 +1,18 @@
#!/usr/bin/env python
'''Powerline terminal prompt example.
'''
from lib.core import Segment
from lib.renderers import TerminalSegmentRenderer
powerline = Segment([
Segment('⭤ SSH', 220, 166, attr=Segment.ATTR_BOLD),
Segment('username', 153, 31),
Segment([
Segment('~'),
Segment('projects'),
Segment('powerline', 231, attr=Segment.ATTR_BOLD),
], 248, 239),
])
print(powerline.render(TerminalSegmentRenderer))