Add initial project files
This commit is contained in:
parent
adb67e1e1f
commit
cf88b086c0
|
@ -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
|
|
@ -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
|
|
@ -0,0 +1,8 @@
|
|||
class SegmentRenderer:
|
||||
def fg(col):
|
||||
raise NotImplementedError
|
||||
|
||||
def bg(col):
|
||||
raise NotImplementedError
|
||||
|
||||
from lib.renderers.terminal import TerminalSegmentRenderer
|
|
@ -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 '[39m'
|
||||
|
||||
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 '[49m'
|
||||
|
||||
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 '[22m'
|
||||
|
||||
ansi_attrs = []
|
||||
if attrs & Segment.ATTR_BOLD:
|
||||
ansi_attrs.append('1')
|
||||
|
||||
if ansi_attrs:
|
||||
return '[{0}m'.format(';'.join(ansi_attrs))
|
||||
|
||||
return ''
|
|
@ -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))
|
Loading…
Reference in New Issue