mirror of
https://github.com/powerline/powerline.git
synced 2025-07-30 01:05:42 +02:00
Move segment rendering to the Renderer class
This commit also updates the extension renderers so that they work correctly with the Renderer class.
This commit is contained in:
parent
8b94e253dd
commit
11ee10851c
@ -1,24 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from segment import mksegment
|
|
||||||
|
|
||||||
|
|
||||||
class Powerline(object):
|
class Powerline(object):
|
||||||
ATTR_BOLD = 1
|
|
||||||
ATTR_ITALIC = 2
|
|
||||||
ATTR_UNDERLINE = 4
|
|
||||||
|
|
||||||
dividers = {
|
|
||||||
'l': {
|
|
||||||
'hard': u'⮀',
|
|
||||||
'soft': u'⮁',
|
|
||||||
},
|
|
||||||
'r': {
|
|
||||||
'hard': u'⮂',
|
|
||||||
'soft': u'⮃',
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
def __init__(self, segments):
|
def __init__(self, segments):
|
||||||
'''Create a new Powerline.
|
'''Create a new Powerline.
|
||||||
|
|
||||||
@ -26,117 +9,8 @@ class Powerline(object):
|
|||||||
dropped from the segment array.
|
dropped from the segment array.
|
||||||
'''
|
'''
|
||||||
self.segments = [segment for segment in segments if segment['contents'] or segment['filler']]
|
self.segments = [segment for segment in segments if segment['contents'] or segment['filler']]
|
||||||
self._hl = {}
|
|
||||||
|
|
||||||
def render(self, renderer, width=None):
|
def render(self, renderer, width=None):
|
||||||
'''Render all the segments with the specified renderer.
|
r = renderer(self.segments)
|
||||||
|
|
||||||
This method loops through the segment array and compares the
|
return r.render(width)
|
||||||
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
|
|
||||||
a time until the line is shorter than the width, or only segments
|
|
||||||
with a negative priority are left. If one or more filler segments are
|
|
||||||
provided they will fill the remaining space until the desired width is
|
|
||||||
reached.
|
|
||||||
'''
|
|
||||||
def render_segments(segments, render_highlighted=True):
|
|
||||||
'''Render a segment array.
|
|
||||||
|
|
||||||
By default this function renders both raw (un-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_highlighted = u''
|
|
||||||
segments_len = len(segments)
|
|
||||||
empty_segment = mksegment()
|
|
||||||
|
|
||||||
for idx, segment in enumerate(segments):
|
|
||||||
prev = segments[idx - 1] if idx > 0 else empty_segment
|
|
||||||
next = segments[idx + 1] if idx < segments_len - 1 else empty_segment
|
|
||||||
|
|
||||||
segment['rendered_raw'] = u''
|
|
||||||
compare = next if segment['side'] == 'l' else prev
|
|
||||||
outer_padding = ' ' if idx == 0 or idx == segments_len - 1 else ''
|
|
||||||
divider_type = 'soft' if compare['bg'] == segment['bg'] else 'hard'
|
|
||||||
divider = self.dividers[segment['side']][divider_type]
|
|
||||||
divider_hl = ''
|
|
||||||
segment_hl = ''
|
|
||||||
|
|
||||||
if render_highlighted:
|
|
||||||
# Generate and cache renderer highlighting
|
|
||||||
if divider_type == 'hard':
|
|
||||||
hl_key = (segment['bg'], compare['bg'])
|
|
||||||
if not hl_key in self._hl:
|
|
||||||
self._hl[hl_key] = renderer.hl(*hl_key)
|
|
||||||
divider_hl = self._hl[hl_key]
|
|
||||||
|
|
||||||
hl_key = (segment['fg'], segment['bg'], segment['attr'])
|
|
||||||
if not hl_key in self._hl:
|
|
||||||
self._hl[hl_key] = renderer.hl(*hl_key)
|
|
||||||
segment_hl = self._hl[hl_key]
|
|
||||||
|
|
||||||
if segment['filler']:
|
|
||||||
# Filler segments shouldn't be padded
|
|
||||||
rendered_highlighted += segment['contents']
|
|
||||||
elif segment['draw_divider'] or divider_type == 'hard':
|
|
||||||
# Draw divider if specified, or if it's a hard divider
|
|
||||||
# Note: Hard dividers are always drawn, regardless of
|
|
||||||
# the draw_divider option
|
|
||||||
if segment['side'] == 'l':
|
|
||||||
segment['rendered_raw'] += outer_padding + segment['contents'] + ' ' + divider + ' '
|
|
||||||
rendered_highlighted += segment_hl + outer_padding + segment['contents'] + ' ' + divider_hl + divider + ' '
|
|
||||||
else:
|
|
||||||
segment['rendered_raw'] += ' ' + divider + ' ' + segment['contents'] + outer_padding
|
|
||||||
rendered_highlighted += ' ' + divider_hl + divider + segment_hl + ' ' + segment['contents'] + outer_padding
|
|
||||||
elif segment['contents']:
|
|
||||||
# Segments without divider
|
|
||||||
if segment['side'] == 'l':
|
|
||||||
segment['rendered_raw'] += outer_padding + segment['contents']
|
|
||||||
rendered_highlighted += segment_hl + outer_padding + segment['contents']
|
|
||||||
else:
|
|
||||||
segment['rendered_raw'] += segment['contents'] + outer_padding
|
|
||||||
rendered_highlighted += segment_hl + segment['contents'] + outer_padding
|
|
||||||
else:
|
|
||||||
# Unknown segment type, skip it
|
|
||||||
continue
|
|
||||||
|
|
||||||
return rendered_highlighted
|
|
||||||
|
|
||||||
rendered_highlighted = 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(self.segments, key=lambda segment: segment['priority'], reverse=True) if segment['priority'] > 0]
|
|
||||||
|
|
||||||
while self._total_len() > width and len(segments_priority):
|
|
||||||
self.segments.remove(segments_priority[0])
|
|
||||||
segments_priority.pop(0)
|
|
||||||
|
|
||||||
# Do another render pass so we can calculate the correct amount of filler space
|
|
||||||
render_segments(self.segments)
|
|
||||||
|
|
||||||
# Distribute the remaining space on the filler segments
|
|
||||||
segments_fillers = [segment for segment in self.segments if segment['filler'] is True]
|
|
||||||
if segments_fillers:
|
|
||||||
segments_fillers_len, segments_fillers_remainder = divmod((width - self._total_len()), len(segments_fillers))
|
|
||||||
segments_fillers_contents = ' ' * segments_fillers_len
|
|
||||||
for segment in segments_fillers:
|
|
||||||
segment['contents'] = segments_fillers_contents
|
|
||||||
# Add remainder whitespace to the first filler segment
|
|
||||||
segments_fillers[0]['contents'] += ' ' * segments_fillers_remainder
|
|
||||||
|
|
||||||
return render_segments(self.segments)
|
|
||||||
|
|
||||||
def _total_len(self):
|
|
||||||
'''Return total/rendered length of all segments.
|
|
||||||
|
|
||||||
This method uses the rendered_raw property of the segments and requires
|
|
||||||
that the segments have been rendered using the render() method first.
|
|
||||||
'''
|
|
||||||
return len(''.join([segment['rendered_raw'] for segment in self.segments]))
|
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
from renderer import TerminalRenderer # NOQA
|
@ -1,10 +1,9 @@
|
|||||||
#!/usr/bin/env python
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from lib.core import Powerline
|
from powerline.renderer import Renderer
|
||||||
from lib.renderers import SegmentRenderer
|
|
||||||
|
|
||||||
|
|
||||||
class TerminalSegmentRenderer(SegmentRenderer):
|
class TerminalRenderer(Renderer):
|
||||||
'''Powerline terminal segment renderer.
|
'''Powerline terminal segment renderer.
|
||||||
'''
|
'''
|
||||||
def hl(self, fg=None, bg=None, attr=None):
|
def hl(self, fg=None, bg=None, attr=None):
|
||||||
@ -32,7 +31,7 @@ class TerminalSegmentRenderer(SegmentRenderer):
|
|||||||
if attr is False:
|
if attr is False:
|
||||||
ansi += [22]
|
ansi += [22]
|
||||||
else:
|
else:
|
||||||
if attr & Powerline.ATTR_BOLD:
|
if attr & Renderer.ATTR_BOLD:
|
||||||
ansi += [1]
|
ansi += [1]
|
||||||
|
|
||||||
return '[{0}m'.format(';'.join(str(attr) for attr in ansi))
|
return '[{0}m'.format(';'.join(str(attr) for attr in ansi))
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
from renderer import TmuxRenderer # NOQA
|
@ -1,10 +1,9 @@
|
|||||||
#!/usr/bin/env python
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from lib.core import Powerline
|
from powerline.renderer import Renderer
|
||||||
from lib.renderers import SegmentRenderer
|
|
||||||
|
|
||||||
|
|
||||||
class TmuxSegmentRenderer(SegmentRenderer):
|
class TmuxRenderer(Renderer):
|
||||||
'''Powerline tmux segment renderer.
|
'''Powerline tmux segment renderer.
|
||||||
'''
|
'''
|
||||||
def hl(self, fg=None, bg=None, attr=None):
|
def hl(self, fg=None, bg=None, attr=None):
|
||||||
@ -22,15 +21,15 @@ class TmuxSegmentRenderer(SegmentRenderer):
|
|||||||
if attr is False:
|
if attr is False:
|
||||||
tmux_attr += ['nobold', 'noitalics', 'nounderscore']
|
tmux_attr += ['nobold', 'noitalics', 'nounderscore']
|
||||||
else:
|
else:
|
||||||
if attr & Powerline.ATTR_BOLD:
|
if attr & Renderer.ATTR_BOLD:
|
||||||
tmux_attr += ['bold']
|
tmux_attr += ['bold']
|
||||||
else:
|
else:
|
||||||
tmux_attr += ['nobold']
|
tmux_attr += ['nobold']
|
||||||
if attr & Powerline.ATTR_ITALIC:
|
if attr & Renderer.ATTR_ITALIC:
|
||||||
tmux_attr += ['italics']
|
tmux_attr += ['italics']
|
||||||
else:
|
else:
|
||||||
tmux_attr += ['noitalics']
|
tmux_attr += ['noitalics']
|
||||||
if attr & Powerline.ATTR_UNDERLINE:
|
if attr & Renderer.ATTR_UNDERLINE:
|
||||||
tmux_attr += ['underscore']
|
tmux_attr += ['underscore']
|
||||||
else:
|
else:
|
||||||
tmux_attr += ['nounderscore']
|
tmux_attr += ['nounderscore']
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
from renderer import VimRenderer # NOQA
|
@ -1,13 +1,13 @@
|
|||||||
#!/usr/bin/env python
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from lib.core import Powerline
|
from powerline.renderer import Renderer
|
||||||
from lib.renderers import SegmentRenderer
|
|
||||||
|
|
||||||
|
|
||||||
class VimSegmentRenderer(SegmentRenderer):
|
class VimRenderer(Renderer):
|
||||||
'''Powerline vim segment renderer.
|
'''Powerline vim segment renderer.
|
||||||
'''
|
'''
|
||||||
def __init__(self):
|
def __init__(self, segments):
|
||||||
|
super(VimRenderer, self).__init__(segments)
|
||||||
self.hl_groups = {}
|
self.hl_groups = {}
|
||||||
|
|
||||||
def hl(self, fg=None, bg=None, attr=None):
|
def hl(self, fg=None, bg=None, attr=None):
|
||||||
@ -41,11 +41,11 @@ class VimSegmentRenderer(SegmentRenderer):
|
|||||||
|
|
||||||
if attr:
|
if attr:
|
||||||
hl_group['attr'] = []
|
hl_group['attr'] = []
|
||||||
if attr & Powerline.ATTR_BOLD:
|
if attr & Renderer.ATTR_BOLD:
|
||||||
hl_group['attr'].append('bold')
|
hl_group['attr'].append('bold')
|
||||||
if attr & Powerline.ATTR_ITALIC:
|
if attr & Renderer.ATTR_ITALIC:
|
||||||
hl_group['attr'].append('italic')
|
hl_group['attr'].append('italic')
|
||||||
if attr & Powerline.ATTR_UNDERLINE:
|
if attr & Renderer.ATTR_UNDERLINE:
|
||||||
hl_group['attr'].append('underline')
|
hl_group['attr'].append('underline')
|
||||||
|
|
||||||
hl_group['name'] = 'Pl_' + \
|
hl_group['name'] = 'Pl_' + \
|
||||||
|
@ -1 +1,140 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from segment import mksegment
|
||||||
|
|
||||||
|
|
||||||
|
class Renderer(object):
|
||||||
|
ATTR_BOLD = 1
|
||||||
|
ATTR_ITALIC = 2
|
||||||
|
ATTR_UNDERLINE = 4
|
||||||
|
|
||||||
|
dividers = {
|
||||||
|
'l': {
|
||||||
|
'hard': u'⮀',
|
||||||
|
'soft': u'⮁',
|
||||||
|
},
|
||||||
|
'r': {
|
||||||
|
'hard': u'⮂',
|
||||||
|
'soft': u'⮃',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(self, segments):
|
||||||
|
self.segments = segments
|
||||||
|
self._hl = {}
|
||||||
|
|
||||||
|
def render(self, width=None):
|
||||||
|
'''Render all the segments with the specified renderer.
|
||||||
|
|
||||||
|
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
|
||||||
|
a time until the line is shorter than the width, or only segments
|
||||||
|
with a negative priority are left. If one or more filler segments are
|
||||||
|
provided they will fill the remaining space until the desired width is
|
||||||
|
reached.
|
||||||
|
'''
|
||||||
|
def render_segments(segments, render_highlighted=True):
|
||||||
|
'''Render a segment array.
|
||||||
|
|
||||||
|
By default this function renders both raw (un-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_highlighted = u''
|
||||||
|
segments_len = len(segments)
|
||||||
|
empty_segment = mksegment()
|
||||||
|
|
||||||
|
for idx, segment in enumerate(segments):
|
||||||
|
prev = segments[idx - 1] if idx > 0 else empty_segment
|
||||||
|
next = segments[idx + 1] if idx < segments_len - 1 else empty_segment
|
||||||
|
|
||||||
|
segment['rendered_raw'] = u''
|
||||||
|
compare = next if segment['side'] == 'l' else prev
|
||||||
|
outer_padding = ' ' if idx == 0 or idx == segments_len - 1 else ''
|
||||||
|
divider_type = 'soft' if compare['bg'] == segment['bg'] else 'hard'
|
||||||
|
divider = self.dividers[segment['side']][divider_type]
|
||||||
|
divider_hl = ''
|
||||||
|
segment_hl = ''
|
||||||
|
|
||||||
|
if render_highlighted:
|
||||||
|
# Generate and cache renderer highlighting
|
||||||
|
if divider_type == 'hard':
|
||||||
|
hl_key = (segment['bg'], compare['bg'])
|
||||||
|
if not hl_key in self._hl:
|
||||||
|
self._hl[hl_key] = self.hl(*hl_key)
|
||||||
|
divider_hl = self._hl[hl_key]
|
||||||
|
|
||||||
|
hl_key = (segment['fg'], segment['bg'], segment['attr'])
|
||||||
|
if not hl_key in self._hl:
|
||||||
|
self._hl[hl_key] = self.hl(*hl_key)
|
||||||
|
segment_hl = self._hl[hl_key]
|
||||||
|
|
||||||
|
if segment['filler']:
|
||||||
|
# Filler segments shouldn't be padded
|
||||||
|
rendered_highlighted += segment['contents']
|
||||||
|
elif segment['draw_divider'] or divider_type == 'hard':
|
||||||
|
# Draw divider if specified, or if it's a hard divider
|
||||||
|
# Note: Hard dividers are always drawn, regardless of
|
||||||
|
# the draw_divider option
|
||||||
|
if segment['side'] == 'l':
|
||||||
|
segment['rendered_raw'] += outer_padding + segment['contents'] + ' ' + divider + ' '
|
||||||
|
rendered_highlighted += segment_hl + outer_padding + segment['contents'] + ' ' + divider_hl + divider + ' '
|
||||||
|
else:
|
||||||
|
segment['rendered_raw'] += ' ' + divider + ' ' + segment['contents'] + outer_padding
|
||||||
|
rendered_highlighted += ' ' + divider_hl + divider + segment_hl + ' ' + segment['contents'] + outer_padding
|
||||||
|
elif segment['contents']:
|
||||||
|
# Segments without divider
|
||||||
|
if segment['side'] == 'l':
|
||||||
|
segment['rendered_raw'] += outer_padding + segment['contents']
|
||||||
|
rendered_highlighted += segment_hl + outer_padding + segment['contents']
|
||||||
|
else:
|
||||||
|
segment['rendered_raw'] += segment['contents'] + outer_padding
|
||||||
|
rendered_highlighted += segment_hl + segment['contents'] + outer_padding
|
||||||
|
else:
|
||||||
|
# Unknown segment type, skip it
|
||||||
|
continue
|
||||||
|
|
||||||
|
return rendered_highlighted
|
||||||
|
|
||||||
|
rendered_highlighted = 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(self.segments, key=lambda segment: segment['priority'], reverse=True) if segment['priority'] > 0]
|
||||||
|
|
||||||
|
while self._total_len() > width and len(segments_priority):
|
||||||
|
self.segments.remove(segments_priority[0])
|
||||||
|
segments_priority.pop(0)
|
||||||
|
|
||||||
|
# Do another render pass so we can calculate the correct amount of filler space
|
||||||
|
render_segments(self.segments)
|
||||||
|
|
||||||
|
# Distribute the remaining space on the filler segments
|
||||||
|
segments_fillers = [segment for segment in self.segments if segment['filler'] is True]
|
||||||
|
if segments_fillers:
|
||||||
|
segments_fillers_len, segments_fillers_remainder = divmod((width - self._total_len()), len(segments_fillers))
|
||||||
|
segments_fillers_contents = ' ' * segments_fillers_len
|
||||||
|
for segment in segments_fillers:
|
||||||
|
segment['contents'] = segments_fillers_contents
|
||||||
|
# Add remainder whitespace to the first filler segment
|
||||||
|
segments_fillers[0]['contents'] += ' ' * segments_fillers_remainder
|
||||||
|
|
||||||
|
return render_segments(self.segments)
|
||||||
|
|
||||||
|
def _total_len(self):
|
||||||
|
'''Return total/rendered length of all segments.
|
||||||
|
|
||||||
|
This method uses the rendered_raw property of the segments and requires
|
||||||
|
that the segments have been rendered using the render() method first.
|
||||||
|
'''
|
||||||
|
return len(''.join([segment['rendered_raw'] for segment in self.segments]))
|
||||||
|
|
||||||
|
def hl(self, fg=None, bg=None, attr=None):
|
||||||
|
raise NotImplementedError
|
||||||
|
Loading…
x
Reference in New Issue
Block a user