diff --git a/lib/core.py b/lib/core.py index 77445da4..a4f353da 100644 --- a/lib/core.py +++ b/lib/core.py @@ -1,30 +1,7 @@ # -*- coding: utf-8 -*- -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)) - ''' +class Powerline: dividers = { 'l': { 'hard': '⮀', @@ -36,91 +13,16 @@ class Segment: }, } - ATTR_BOLD = 1 - ATTR_ITALIC = 2 - ATTR_UNDERLINE = 4 - - def __init__(self, contents=None, fg=None, bg=None, attr=None, side=None, draw_divider=None, priority=None, filler=None): - '''Create a new Powerline segment. + def __init__(self, segments): + '''Create a new Powerline. ''' - self.parent = None - - self.contents = contents or '' - self.fg = fg - self.bg = bg - self.attr = attr - self.side = side - self.draw_divider = draw_divider - self.priority = priority - self.filler = filler - - if self.filler: - # Filler segments should never have any dividers - self.draw_divider = False - - # Set the parent property for child segments - for segment in self.contents: - try: - segment.parent = self - except AttributeError: - # Not a Segment node - continue - - def init_attributes(self): - '''Initialize the default attributes for this segment. - - This method is intended to be run when all segments in the segment tree - have the correct parent segment set (i.e. after the root segment has - been instantiated). - ''' - def lookup_attr(attr, default, obj=self): - '''Looks up attributes in the segment tree. - - If the attribute isn't found anywhere, the default argument is used - for this segment. - ''' - # Check if the current object has the attribute defined - obj_attr = getattr(obj, attr) - if obj_attr is None: - try: - # Check if the object's parent has the attribute defined - return lookup_attr(attr, default, obj.parent) - except AttributeError: - # Root node reached - return default - return obj_attr - - # Set default attributes - self.fg = lookup_attr('fg', False) - self.bg = lookup_attr('bg', False) - self.attr = lookup_attr('attr', False) - self.side = lookup_attr('side', 'l') - self.draw_divider = lookup_attr('draw_divider', True) - self.priority = lookup_attr('priority', -1) - self.filler = lookup_attr('filler', False) - - try: - if len(self.fg) == 2: - self.fg = self.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 = [self.fg, cterm_to_hex(self.fg)] - - try: - if len(self.bg) == 2: - self.bg = self.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 = [self.bg, cterm_to_hex(self.bg)] + self.segments = segments def render(self, renderer, width=None): - '''Render the segment and all child segments. + '''Render all the segments with the specified renderer. - 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 divider properties and returns the + This method loops through the segment array and compares the + foreground/background colors and divider properties and returns the rendered statusline as a string. When a width is provided, low-priority segments are dropped one at @@ -129,28 +31,13 @@ class Segment: provided they will fill the remaining space until the desired width is reached. ''' - def flatten(segment): - '''Flatten the segment tree into a one-dimensional array. - ''' - ret = [] - for child_segment in segment.contents: - if isinstance(child_segment.contents, str): - # If the contents of the child segment is a string then - # this is a tree node - child_segment.init_attributes() - ret.append(child_segment) - else: - # This is a segment group that should be flattened - ret += flatten(child_segment) - return ret - - segments = flatten(self) - def render_segments(segments, render_raw=True, render_highlighted=True): - '''Render a one-dimensional segment array. + '''Render a segment array. By default this function renders both raw (un-highlighted segments - used for calculating final width) and highlighted segments. + used for calculating final width) and highlighted segments. The raw + rendering is used for calculating the total width for dropping + low-priority segments. ''' rendered_raw = '' rendered_highlighted = '' @@ -207,29 +94,67 @@ class Segment: 'raw': rendered_raw, } - rendered = render_segments(segments) + rendered = render_segments(self.segments) if not width: # No width specified, so we don't need to crop or pad anything return rendered['highlighted'] # Create an ordered list of segments that can be dropped - segments_priority = [segment for segment in sorted(segments, key=lambda segment: segment.priority, reverse=True) if segment.priority > 0] + segments_priority = [segment for segment in sorted(self.segments, key=lambda segment: segment.priority, reverse=True) if segment.priority > 0] while len(rendered['raw'].decode('utf-8')) > width and len(segments_priority): - segments.remove(segments_priority[0]) + self.segments.remove(segments_priority[0]) segments_priority.pop(0) - rendered = render_segments(segments, render_highlighted=False) + rendered = render_segments(self.segments, render_highlighted=False) # Distribute the remaining space on the filler segments - segments_fillers = [segment for segment in segments if segment.filler is True] + segments_fillers = [segment for segment in self.segments if segment.filler is True] if segments_fillers: segments_fillers_contents = ' ' * int((width - len(rendered['raw'].decode('utf-8'))) / len(segments_fillers)) for segment in segments_fillers: segment.contents = segments_fillers_contents # Do a final render now that we have handled the cropping and padding - rendered = render_segments(segments, render_raw=False) + rendered = render_segments(self.segments, render_raw=False) return rendered['highlighted'] + + +class Segment: + ATTR_BOLD = 1 + ATTR_ITALIC = 2 + ATTR_UNDERLINE = 4 + + def __init__(self, contents=None, fg=False, bg=False, attr=False, side='l', draw_divider=True, priority=-1, filler=False): + '''Create a new Powerline segment. + ''' + self.contents = str(contents or '') + self.fg = fg + self.bg = bg + self.attr = attr + self.side = side + self.draw_divider = draw_divider + self.priority = priority + self.filler = filler + + if self.filler: + # Filler segments should never have any dividers + self.draw_divider = False + + try: + if len(self.fg) != 2: + raise TypeError + 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 = [self.fg, cterm_to_hex(self.fg)] + + try: + if len(self.bg) != 2: + raise TypeError + 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 = [self.bg, cterm_to_hex(self.bg)] diff --git a/powerline-terminal-example.py b/powerline-terminal-example.py index 0677d768..8ae52cfc 100755 --- a/powerline-terminal-example.py +++ b/powerline-terminal-example.py @@ -2,17 +2,16 @@ '''Powerline terminal prompt example. ''' -from lib.core import Segment +from lib.core import Powerline, Segment from lib.renderers import TerminalSegmentRenderer -powerline = Segment([ +powerline = Powerline([ 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), + Segment('~', 248, 239), + Segment('projects', 248, 239), + Segment('powerline', 231, 239, attr=Segment.ATTR_BOLD), + Segment(filler=True), ]) print(powerline.render(TerminalSegmentRenderer())) diff --git a/powerline-vim-example.vim b/powerline-vim-example.vim index 4a092262..7f2da064 100644 --- a/powerline-vim-example.vim +++ b/powerline-vim-example.vim @@ -12,7 +12,7 @@ import re import sys sys.path.append('.') -from lib.core import Segment +from lib.core import Powerline, Segment from lib.renderers import VimSegmentRenderer winwidth = int(vim.eval('winwidth(0)')) @@ -47,26 +47,20 @@ filepath = os.path.split(vim.eval('expand("%:~:.")')) if filepath[0]: filepath[0] += os.sep -powerline = Segment([ +powerline = Powerline([ Segment(mode, 22, 148, attr=Segment.ATTR_BOLD), Segment('⭠ ' + branch, 250, 240, priority=10), - Segment([ - Segment(filepath[0], draw_divider=False, priority=5), - Segment(filepath[1], 231, attr=Segment.ATTR_BOLD), - ], 250, 240), - Segment(filler=True), - Segment([ - Segment(vim.eval('&ff'), priority=50), - Segment(vim.eval('&fenc'), priority=50), - Segment(vim.eval('&ft'), priority=50), - Segment(str(line_percent).rjust(3) + '%', line_percent_color, 240, priority=30), - Segment([ - Segment('⭡ ', 239), - Segment(str(line_current).rjust(3), attr=Segment.ATTR_BOLD, draw_divider=False), - Segment(':' + str(col_current).ljust(2), 244, priority=30, draw_divider=False), - ], 235, 252), - ], 247, side='r'), -], fg=236, bg=236) + Segment(filepath[0], 250, 240, draw_divider=False, priority=5), + Segment(filepath[1], 231, 240, attr=Segment.ATTR_BOLD), + Segment(filler=True, fg=236, bg=236), + Segment(vim.eval('&ff'), 247, 236, side='r', priority=50), + Segment(vim.eval('&fenc'), 247, 236, side='r', priority=50), + Segment(vim.eval('&ft'), 247, 236, side='r', priority=50), + Segment(str(line_percent).rjust(3) + '%', line_percent_color, 240, side='r', priority=30), + Segment('⭡ ', 239, 252, side='r'), + Segment(str(line_current).rjust(3), 235, 252, attr=Segment.ATTR_BOLD, side='r', draw_divider=False), + Segment(':' + str(col_current).ljust(2), 244, 252, side='r', priority=30, draw_divider=False), +]) renderer = VimSegmentRenderer() stl = powerline.render(renderer, winwidth)