From 4d225179d092136a03d60840ae628e4db895fa34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Wed, 5 Dec 2012 16:37:16 +0100 Subject: [PATCH 01/31] Reorganize files --- lib/renderers/__init__.py | 7 ------- {lib => powerline}/__init__.py | 0 lib/colors.py => powerline/colorscheme.py | 2 ++ {lib => powerline}/core.py | 0 {vim => powerline/ext}/__init__.py | 0 powerline/ext/terminal/__init__.py | 0 .../terminal.py => powerline/ext/terminal/renderer.py | 0 powerline/ext/tmux/__init__.py | 0 lib/renderers/tmux.py => powerline/ext/tmux/renderer.py | 0 powerline/ext/vim/__init__.py | 0 {vim => powerline/ext/vim}/bindings.py | 0 lib/renderers/vim.py => powerline/ext/vim/renderer.py | 0 {vim => powerline/ext/vim}/segments/__init__.py | 0 {vim => powerline/ext/vim}/segments/core.py | 4 ++-- powerline/renderer.py | 1 + powerline/segment.py | 1 + powerline/theme.py | 1 + 17 files changed, 7 insertions(+), 9 deletions(-) delete mode 100644 lib/renderers/__init__.py rename {lib => powerline}/__init__.py (100%) rename lib/colors.py => powerline/colorscheme.py (99%) rename {lib => powerline}/core.py (100%) rename {vim => powerline/ext}/__init__.py (100%) create mode 100644 powerline/ext/terminal/__init__.py rename lib/renderers/terminal.py => powerline/ext/terminal/renderer.py (100%) create mode 100644 powerline/ext/tmux/__init__.py rename lib/renderers/tmux.py => powerline/ext/tmux/renderer.py (100%) create mode 100644 powerline/ext/vim/__init__.py rename {vim => powerline/ext/vim}/bindings.py (100%) rename lib/renderers/vim.py => powerline/ext/vim/renderer.py (100%) rename {vim => powerline/ext/vim}/segments/__init__.py (100%) rename {vim => powerline/ext/vim}/segments/core.py (97%) create mode 100644 powerline/renderer.py create mode 100644 powerline/segment.py create mode 100644 powerline/theme.py diff --git a/lib/renderers/__init__.py b/lib/renderers/__init__.py deleted file mode 100644 index 3c7c2387..00000000 --- a/lib/renderers/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -class SegmentRenderer: - def hl(self, fg=None, bg=None, attr=None): - raise NotImplementedError - -from lib.renderers.terminal import TerminalSegmentRenderer -from lib.renderers.tmux import TmuxSegmentRenderer -from lib.renderers.vim import VimSegmentRenderer diff --git a/lib/__init__.py b/powerline/__init__.py similarity index 100% rename from lib/__init__.py rename to powerline/__init__.py diff --git a/lib/colors.py b/powerline/colorscheme.py similarity index 99% rename from lib/colors.py rename to powerline/colorscheme.py index c0a34e45..cb345904 100644 --- a/lib/colors.py +++ b/powerline/colorscheme.py @@ -1,3 +1,5 @@ +# -*- coding: utf-8 -*- + cterm_to_hex = { 16: 0x000000, 17: 0x00005f, 18: 0x000087, 19: 0x0000af, 20: 0x0000d7, 21: 0x0000ff, 22: 0x005f00, 23: 0x005f5f, 24: 0x005f87, 25: 0x005faf, 26: 0x005fd7, 27: 0x005fff, diff --git a/lib/core.py b/powerline/core.py similarity index 100% rename from lib/core.py rename to powerline/core.py diff --git a/vim/__init__.py b/powerline/ext/__init__.py similarity index 100% rename from vim/__init__.py rename to powerline/ext/__init__.py diff --git a/powerline/ext/terminal/__init__.py b/powerline/ext/terminal/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/lib/renderers/terminal.py b/powerline/ext/terminal/renderer.py similarity index 100% rename from lib/renderers/terminal.py rename to powerline/ext/terminal/renderer.py diff --git a/powerline/ext/tmux/__init__.py b/powerline/ext/tmux/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/lib/renderers/tmux.py b/powerline/ext/tmux/renderer.py similarity index 100% rename from lib/renderers/tmux.py rename to powerline/ext/tmux/renderer.py diff --git a/powerline/ext/vim/__init__.py b/powerline/ext/vim/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/vim/bindings.py b/powerline/ext/vim/bindings.py similarity index 100% rename from vim/bindings.py rename to powerline/ext/vim/bindings.py diff --git a/lib/renderers/vim.py b/powerline/ext/vim/renderer.py similarity index 100% rename from lib/renderers/vim.py rename to powerline/ext/vim/renderer.py diff --git a/vim/segments/__init__.py b/powerline/ext/vim/segments/__init__.py similarity index 100% rename from vim/segments/__init__.py rename to powerline/ext/vim/segments/__init__.py diff --git a/vim/segments/core.py b/powerline/ext/vim/segments/core.py similarity index 97% rename from vim/segments/core.py rename to powerline/ext/vim/segments/core.py index e5586d96..a08d1443 100644 --- a/vim/segments/core.py +++ b/powerline/ext/vim/segments/core.py @@ -74,7 +74,7 @@ def readonly_indicator(text=u'⭤'): return text if int(vim.eval('&readonly')) else None -def branch(symbol=u'⭠'): +def branch(): '''Return VCS branch. TODO: Expand this function to handle several VCS plugins. @@ -87,7 +87,7 @@ def branch(symbol=u'⭠'): except TypeError: pass - return symbol + ' ' + branch if branch else None + return branch if branch else None def file_directory(): diff --git a/powerline/renderer.py b/powerline/renderer.py new file mode 100644 index 00000000..40a96afc --- /dev/null +++ b/powerline/renderer.py @@ -0,0 +1 @@ +# -*- coding: utf-8 -*- diff --git a/powerline/segment.py b/powerline/segment.py new file mode 100644 index 00000000..40a96afc --- /dev/null +++ b/powerline/segment.py @@ -0,0 +1 @@ +# -*- coding: utf-8 -*- diff --git a/powerline/theme.py b/powerline/theme.py new file mode 100644 index 00000000..40a96afc --- /dev/null +++ b/powerline/theme.py @@ -0,0 +1 @@ +# -*- coding: utf-8 -*- From d4d84a43467b13dbb0a362024e5567b7538e51b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Wed, 5 Dec 2012 17:35:21 +0100 Subject: [PATCH 02/31] Move mksegment out of core --- powerline/core.py | 22 +--------------------- powerline/segment.py | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/powerline/core.py b/powerline/core.py index d3c407d6..6159f473 100644 --- a/powerline/core.py +++ b/powerline/core.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -from lib.colors import cterm_to_hex +from segment import mksegment class Powerline(object): @@ -140,23 +140,3 @@ class Powerline(object): that the segments have been rendered using the render() method first. ''' return len(''.join([segment['rendered_raw'] for segment in self.segments])) - - -def mksegment(contents=None, cterm_fg=False, cterm_bg=False, attr=False, hex_fg=False, hex_bg=False, side='l', draw_divider=True, priority=-1, filler=False): - '''Convenience wrapper for segment generation. - ''' - try: - contents = unicode(contents or u'') - except UnicodeDecodeError: - contents = contents.decode('utf-8') or u'' - - return { - 'contents': contents, - 'fg': (cterm_fg, hex_fg or cterm_to_hex.get(cterm_fg, 0xffffff)), - 'bg': (cterm_bg, hex_bg or cterm_to_hex.get(cterm_bg, 0x000000)), - 'attr': attr, - 'side': side, - 'draw_divider': False if filler else draw_divider, - 'priority': priority, - 'filler': filler, - } diff --git a/powerline/segment.py b/powerline/segment.py index 40a96afc..e8955a8b 100644 --- a/powerline/segment.py +++ b/powerline/segment.py @@ -1 +1,23 @@ # -*- coding: utf-8 -*- + +from colorscheme import cterm_to_hex + + +def mksegment(contents=None, cterm_fg=False, cterm_bg=False, attr=False, hex_fg=False, hex_bg=False, side='l', draw_divider=True, priority=-1, filler=False): + '''Convenience wrapper for segment generation. + ''' + try: + contents = unicode(contents or u'') + except UnicodeDecodeError: + contents = contents.decode('utf-8') or u'' + + return { + 'contents': contents, + 'fg': (cterm_fg, hex_fg or cterm_to_hex.get(cterm_fg, 0xffffff)), + 'bg': (cterm_bg, hex_bg or cterm_to_hex.get(cterm_bg, 0x000000)), + 'attr': attr, + 'side': side, + 'draw_divider': False if filler else draw_divider, + 'priority': priority, + 'filler': filler, + } From d72265cabbf9b54d246b3b2032efc2f8f47536de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Wed, 5 Dec 2012 17:45:41 +0100 Subject: [PATCH 03/31] Add colorscheme/theme placeholders --- powerline/colorschemes/default.json | 0 powerline/themes/vim/default.json | 0 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 powerline/colorschemes/default.json create mode 100644 powerline/themes/vim/default.json diff --git a/powerline/colorschemes/default.json b/powerline/colorschemes/default.json new file mode 100644 index 00000000..e69de29b diff --git a/powerline/themes/vim/default.json b/powerline/themes/vim/default.json new file mode 100644 index 00000000..e69de29b From bcde4f6293988af7fa0949b2b75a57caf2f0b28a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Wed, 5 Dec 2012 17:56:19 +0100 Subject: [PATCH 04/31] Add default vim theme --- powerline/themes/vim/default.json | 99 +++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) diff --git a/powerline/themes/vim/default.json b/powerline/themes/vim/default.json index e69de29b..17388119 100644 --- a/powerline/themes/vim/default.json +++ b/powerline/themes/vim/default.json @@ -0,0 +1,99 @@ +{ + "name": "default", + "segments": { + "left": [ + { + "type": "function", + "name": "mode" + }, + { + "type": "function", + "name": "paste_indicator", + "exclude_modes": ["nc"], + "priority": 10 + }, + { + "type": "function", + "name": "branch", + "exclude_modes": ["nc"], + "priority": 60, + "before": "⭠ " + }, + { + "type": "function", + "name": "readonly_indicator", + "exclude_modes": ["nc"], + "draw_divider": false + }, + { + "type": "function", + "name": "file_directory", + "priority" 40, + "draw_divider": false + }, + { + "type": "function", + "name": "file_name", + "draw_divider": false + }, + { + "type": "function", + "name": "modified_indicator", + "draw_divider": false + }, + { + "type": "function", + "name": "modified_indicator", + "args": { "text": "+" }, + "exclude_modes": ["nc"] + }, + { + "type": "filler", + "highlight": ["background"] + }, + ], + "right": [ + { + "type": "function", + "name": "file_format", + "draw_divider": false, + "exclude_modes": ["nc"], + "priority" 50 + }, + { + "type": "function", + "name": "file_encoding", + "exclude_modes": ["nc"], + "priority" 50 + }, + { + "type": "function", + "name": "file_type", + "exclude_modes": ["nc"], + "priority" 50 + }, + { + "type": "function", + "name": "line_percent", + "args": { "gradient": true }, + "priority": 30 + }, + { + "type": "string", + "contents": "⭡ ", + "highlight": ["line_current_symbol", "line_current"] + }, + { + "type": "function", + "name": "line_current", + "draw_divider": false + }, + { + "type": "function", + "name": "col_current", + "draw_divider": false, + "priority": 30 + }, + ] + } +} From 8b94e253dda848390d6c2c7f37d092885f1d5503 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Wed, 5 Dec 2012 17:56:38 +0100 Subject: [PATCH 05/31] Add default colorscheme skeleton --- powerline/colorschemes/default.json | 57 +++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/powerline/colorschemes/default.json b/powerline/colorschemes/default.json index e69de29b..8a792192 100644 --- a/powerline/colorschemes/default.json +++ b/powerline/colorschemes/default.json @@ -0,0 +1,57 @@ +{ + "name": "default", + "colors": { + "black": 16, + "white": 231, + + "darkestgreen": 22, + "darkgreen": 28, + "mediumgreen": 70, + "brightgreen": 148, + + "darkestcyan": 23, + "mediumcyan": 117, + + "darkestblue": 24, + "darkblue": 31, + + "darkestred": 52, + "darkred": 88, + "mediumred": 124, + "brightred": 160, + "brightestred": 196, + + "darkestpurple": 55, + "mediumpurple": 98, + "brightpurple": 189, + + "brightorange": 208, + "brightestorange": 214, + + "gray0": 233, + "gray1": 235, + "gray2": 236, + "gray3": 239, + "gray4": 240, + "gray5": 241, + "gray6": 244, + "gray7": 245, + "gray8": 247, + "gray9": 250, + "gray10": 252 + } + "groups": { + "background": { "fg": "white", "bg": "gray2" }, + "mode_indicator": { "fg": "darkestgreen", "bg": "brightgreen", "attr": ["bold"] } + } + "mode_translations": { + "i": { + "colors": { + "gray2": "darkestblue" + }, + "groups": { + "mode_indicator": { "fg": "darkestcyan", "bg": "white", "attr": ["bold"] } + } + } + } +} From 11ee10851c220650b346b478d5adafb6a60d4470 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Thu, 6 Dec 2012 14:23:24 +0100 Subject: [PATCH 06/31] Move segment rendering to the Renderer class This commit also updates the extension renderers so that they work correctly with the Renderer class. --- powerline/core.py | 130 +-------------------------- powerline/ext/terminal/__init__.py | 1 + powerline/ext/terminal/renderer.py | 9 +- powerline/ext/tmux/__init__.py | 1 + powerline/ext/tmux/renderer.py | 13 ++- powerline/ext/vim/__init__.py | 1 + powerline/ext/vim/renderer.py | 16 ++-- powerline/renderer.py | 139 +++++++++++++++++++++++++++++ 8 files changed, 162 insertions(+), 148 deletions(-) diff --git a/powerline/core.py b/powerline/core.py index 6159f473..04946d39 100644 --- a/powerline/core.py +++ b/powerline/core.py @@ -1,24 +1,7 @@ # -*- coding: utf-8 -*- -from segment import mksegment - 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): '''Create a new Powerline. @@ -26,117 +9,8 @@ class Powerline(object): dropped from the segment array. ''' self.segments = [segment for segment in segments if segment['contents'] or segment['filler']] - self._hl = {} 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 - 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])) + return r.render(width) diff --git a/powerline/ext/terminal/__init__.py b/powerline/ext/terminal/__init__.py index e69de29b..5bf82fe6 100644 --- a/powerline/ext/terminal/__init__.py +++ b/powerline/ext/terminal/__init__.py @@ -0,0 +1 @@ +from renderer import TerminalRenderer # NOQA diff --git a/powerline/ext/terminal/renderer.py b/powerline/ext/terminal/renderer.py index ff55dff2..0c5c5ebe 100644 --- a/powerline/ext/terminal/renderer.py +++ b/powerline/ext/terminal/renderer.py @@ -1,10 +1,9 @@ -#!/usr/bin/env python +# -*- coding: utf-8 -*- -from lib.core import Powerline -from lib.renderers import SegmentRenderer +from powerline.renderer import Renderer -class TerminalSegmentRenderer(SegmentRenderer): +class TerminalRenderer(Renderer): '''Powerline terminal segment renderer. ''' def hl(self, fg=None, bg=None, attr=None): @@ -32,7 +31,7 @@ class TerminalSegmentRenderer(SegmentRenderer): if attr is False: ansi += [22] else: - if attr & Powerline.ATTR_BOLD: + if attr & Renderer.ATTR_BOLD: ansi += [1] return '[{0}m'.format(';'.join(str(attr) for attr in ansi)) diff --git a/powerline/ext/tmux/__init__.py b/powerline/ext/tmux/__init__.py index e69de29b..c8e23c53 100644 --- a/powerline/ext/tmux/__init__.py +++ b/powerline/ext/tmux/__init__.py @@ -0,0 +1 @@ +from renderer import TmuxRenderer # NOQA diff --git a/powerline/ext/tmux/renderer.py b/powerline/ext/tmux/renderer.py index 07db9ff1..458cb191 100644 --- a/powerline/ext/tmux/renderer.py +++ b/powerline/ext/tmux/renderer.py @@ -1,10 +1,9 @@ -#!/usr/bin/env python +# -*- coding: utf-8 -*- -from lib.core import Powerline -from lib.renderers import SegmentRenderer +from powerline.renderer import Renderer -class TmuxSegmentRenderer(SegmentRenderer): +class TmuxRenderer(Renderer): '''Powerline tmux segment renderer. ''' def hl(self, fg=None, bg=None, attr=None): @@ -22,15 +21,15 @@ class TmuxSegmentRenderer(SegmentRenderer): if attr is False: tmux_attr += ['nobold', 'noitalics', 'nounderscore'] else: - if attr & Powerline.ATTR_BOLD: + if attr & Renderer.ATTR_BOLD: tmux_attr += ['bold'] else: tmux_attr += ['nobold'] - if attr & Powerline.ATTR_ITALIC: + if attr & Renderer.ATTR_ITALIC: tmux_attr += ['italics'] else: tmux_attr += ['noitalics'] - if attr & Powerline.ATTR_UNDERLINE: + if attr & Renderer.ATTR_UNDERLINE: tmux_attr += ['underscore'] else: tmux_attr += ['nounderscore'] diff --git a/powerline/ext/vim/__init__.py b/powerline/ext/vim/__init__.py index e69de29b..69d94051 100644 --- a/powerline/ext/vim/__init__.py +++ b/powerline/ext/vim/__init__.py @@ -0,0 +1 @@ +from renderer import VimRenderer # NOQA diff --git a/powerline/ext/vim/renderer.py b/powerline/ext/vim/renderer.py index 07a4dbb2..dc64f2d1 100644 --- a/powerline/ext/vim/renderer.py +++ b/powerline/ext/vim/renderer.py @@ -1,13 +1,13 @@ -#!/usr/bin/env python +# -*- coding: utf-8 -*- -from lib.core import Powerline -from lib.renderers import SegmentRenderer +from powerline.renderer import Renderer -class VimSegmentRenderer(SegmentRenderer): +class VimRenderer(Renderer): '''Powerline vim segment renderer. ''' - def __init__(self): + def __init__(self, segments): + super(VimRenderer, self).__init__(segments) self.hl_groups = {} def hl(self, fg=None, bg=None, attr=None): @@ -41,11 +41,11 @@ class VimSegmentRenderer(SegmentRenderer): if attr: hl_group['attr'] = [] - if attr & Powerline.ATTR_BOLD: + if attr & Renderer.ATTR_BOLD: hl_group['attr'].append('bold') - if attr & Powerline.ATTR_ITALIC: + if attr & Renderer.ATTR_ITALIC: hl_group['attr'].append('italic') - if attr & Powerline.ATTR_UNDERLINE: + if attr & Renderer.ATTR_UNDERLINE: hl_group['attr'].append('underline') hl_group['name'] = 'Pl_' + \ diff --git a/powerline/renderer.py b/powerline/renderer.py index 40a96afc..eb06e135 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -1 +1,140 @@ # -*- 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 From 636f1719fb2b70e5d8920a3750c7b738cc693ef0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Thu, 6 Dec 2012 14:59:50 +0100 Subject: [PATCH 07/31] Only drop segments whose contents are None --- powerline/core.py | 4 ++-- powerline/segment.py | 9 +++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/powerline/core.py b/powerline/core.py index 04946d39..b0c8bd3d 100644 --- a/powerline/core.py +++ b/powerline/core.py @@ -5,10 +5,10 @@ class Powerline(object): def __init__(self, segments): '''Create a new Powerline. - Segments that have empty contents and aren't filler segments are + Segments that aren't filler segments and whose contents aren't None are 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'] is not None or segment['filler']] def render(self, renderer, width=None): r = renderer(self.segments) diff --git a/powerline/segment.py b/powerline/segment.py index e8955a8b..512ea574 100644 --- a/powerline/segment.py +++ b/powerline/segment.py @@ -6,10 +6,11 @@ from colorscheme import cterm_to_hex def mksegment(contents=None, cterm_fg=False, cterm_bg=False, attr=False, hex_fg=False, hex_bg=False, side='l', draw_divider=True, priority=-1, filler=False): '''Convenience wrapper for segment generation. ''' - try: - contents = unicode(contents or u'') - except UnicodeDecodeError: - contents = contents.decode('utf-8') or u'' + if contents is not None or filler: + try: + contents = unicode(contents or u'') + except UnicodeDecodeError: + contents = contents.decode('utf-8') or u'' return { 'contents': contents, From 1cb6aeab0d86ce79ecbb26ecc705902a0b360b93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Thu, 6 Dec 2012 15:00:44 +0100 Subject: [PATCH 08/31] Create renderer property for Powerline class --- powerline/core.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/powerline/core.py b/powerline/core.py index b0c8bd3d..6b7c7d04 100644 --- a/powerline/core.py +++ b/powerline/core.py @@ -8,9 +8,10 @@ class Powerline(object): Segments that aren't filler segments and whose contents aren't None are dropped from the segment array. ''' + self.renderer = None # FIXME This should be assigned here based on the current configuration self.segments = [segment for segment in segments if segment['contents'] is not None or segment['filler']] def render(self, renderer, width=None): - r = renderer(self.segments) + self.renderer = renderer(self.segments) - return r.render(width) + return self.renderer.render(width) From 5d2214db52ea1481527b91101ec270ab708fd41a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Thu, 6 Dec 2012 15:02:37 +0100 Subject: [PATCH 09/31] Make the examples work with the new project structure --- examples/terminal/{powerline.py => pl.py} | 11 +-- examples/tmux/{powerline.py => pl.py} | 11 +-- examples/vim/{powerline.py => pl.py} | 84 ++++++++--------------- examples/vim/powerline.vim | 2 +- 4 files changed, 42 insertions(+), 66 deletions(-) rename examples/terminal/{powerline.py => pl.py} (53%) rename examples/tmux/{powerline.py => pl.py} (55%) rename examples/vim/{powerline.py => pl.py} (69%) diff --git a/examples/terminal/powerline.py b/examples/terminal/pl.py similarity index 53% rename from examples/terminal/powerline.py rename to examples/terminal/pl.py index dafbf340..cdecdaec 100755 --- a/examples/terminal/powerline.py +++ b/examples/terminal/pl.py @@ -7,16 +7,17 @@ import os import sys sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) -from lib.core import Powerline, mksegment -from lib.renderers import TerminalSegmentRenderer +from powerline.core import Powerline +from powerline.segment import mksegment +from powerline.ext.terminal import TerminalRenderer powerline = Powerline([ - mksegment('⭤ SSH', 220, 166, attr=Powerline.ATTR_BOLD), + mksegment('⭤ SSH', 220, 166, attr=TerminalRenderer.ATTR_BOLD), mksegment('username', 153, 31), mksegment('~', 248, 239), mksegment('projects', 248, 239), - mksegment('powerline', 231, 239, attr=Powerline.ATTR_BOLD), + mksegment('powerline', 231, 239, attr=TerminalRenderer.ATTR_BOLD), mksegment(filler=True), ]) -print(powerline.render(TerminalSegmentRenderer())) +print(powerline.render(TerminalRenderer)) diff --git a/examples/tmux/powerline.py b/examples/tmux/pl.py similarity index 55% rename from examples/tmux/powerline.py rename to examples/tmux/pl.py index 7f0a5da5..060fdbf3 100755 --- a/examples/tmux/powerline.py +++ b/examples/tmux/pl.py @@ -9,15 +9,16 @@ import os import sys sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) -from lib.core import Powerline, mksegment -from lib.renderers import TmuxSegmentRenderer +from powerline.core import Powerline +from powerline.segment import mksegment +from powerline.ext.tmux import TmuxRenderer powerline = Powerline([ - mksegment('⭤ SSH', 220, 166, attr=Powerline.ATTR_BOLD), + mksegment('⭤ SSH', 220, 166, attr=TmuxRenderer.ATTR_BOLD), mksegment('username', 153, 31), mksegment('23:45', 248, 239), - mksegment('10.0.0.110', 231, 239, attr=Powerline.ATTR_BOLD), + mksegment('10.0.0.110', 231, 239, attr=TmuxRenderer.ATTR_BOLD), mksegment(filler=True, cterm_fg=236, cterm_bg=236), ]) -print(powerline.render(TmuxSegmentRenderer()).encode('utf-8')) +print(powerline.render(TmuxRenderer).encode('utf-8')) diff --git a/examples/vim/powerline.py b/examples/vim/pl.py similarity index 69% rename from examples/vim/powerline.py rename to examples/vim/pl.py index 42a35e11..2ef16e03 100644 --- a/examples/vim/powerline.py +++ b/examples/vim/pl.py @@ -3,8 +3,10 @@ import vim import os -from lib.core import Powerline, mksegment -from lib.renderers import VimSegmentRenderer +from powerline.core import Powerline +from powerline.segment import mksegment +from powerline.ext.vim import VimRenderer +from powerline.ext.vim.bindings import vim_get_func modes = { 'n': 'NORMAL', @@ -30,53 +32,19 @@ modes = { # We need to replace this private use glyph with a double-percent later percent_placeholder = u'' -if hasattr(vim, 'bindeval'): - # This branch is used to avoid invoking vim parser as much as possible - - def get_vim_func(f, rettype=None): - try: - return vim.bindeval('function("' + f + '")') - except vim.error: - return None - - vim_globals = vim.bindeval('g:') - - def set_global_var(var, val): - vim_globals[var] = val -else: - import json - - class VimFunc(object): - __slots__ = ('f', 'rettype') - - def __init__(self, f, rettype=None): - self.f = f - self.rettype = rettype - - def __call__(self, *args): - r = vim.eval(self.f + '(' + json.dumps(args)[1:-1] + ')') - if self.rettype: - return self.rettype(r) - return r - - get_vim_func = VimFunc - - def set_global_var(var, val): # NOQA - vim.command('let g:{0}={1}'.format(var, json.dumps(val))) - vim_funcs = { - 'winwidth': get_vim_func('winwidth', rettype=int), - 'mode': get_vim_func('mode'), - 'fghead': get_vim_func('fugitive#head'), - 'line': get_vim_func('line', rettype=int), - 'col': get_vim_func('col', rettype=int), - 'expand': get_vim_func('expand'), - 'tbcurtag': get_vim_func('tagbar#currenttag'), - 'hlexists': get_vim_func('hlexists', rettype=int), + 'winwidth': vim_get_func('winwidth', rettype=int), + 'mode': vim_get_func('mode'), + 'fghead': vim_get_func('fugitive#head'), + 'line': vim_get_func('line', rettype=int), + 'col': vim_get_func('col', rettype=int), + 'expand': vim_get_func('expand'), + 'tbcurtag': vim_get_func('tagbar#currenttag'), + 'hlexists': vim_get_func('hlexists', rettype=int), } -getwinvar = get_vim_func('getwinvar') -setwinvar = get_vim_func('setwinvar') +getwinvar = vim_get_func('getwinvar') +setwinvar = vim_get_func('setwinvar') def statusline(winnr): @@ -85,7 +53,7 @@ def statusline(winnr): current = getwinvar(winnr, 'current') windata = getwinvar(winnr, 'powerline') - if current: + if current or not windata.keys(): # Recreate segment data for each redraw if we're in the current window line_current = vim_funcs['line']('.') line_end = vim_funcs['line']('$') @@ -128,7 +96,7 @@ def statusline(winnr): currenttag = '' windata = { - 'paste': vim.eval('&paste ? "PASTE" : ""'), + 'paste': vim.eval('&paste ? "PASTE" : ""') or None, 'branch': branch, 'readonly': readonly, 'filepath': filepath, @@ -145,6 +113,9 @@ def statusline(winnr): 'colcurrent': ':' + str(col_current).ljust(2), } + # Horrible workaround for missing None type for vimdicts + windata = {k: v if v is not None else '__None__' for k, v in windata.items()} + setwinvar(winnr, 'powerline', windata) mode = modes[vim_funcs['mode']()] @@ -152,14 +123,17 @@ def statusline(winnr): if not current: mode = None + # Horrible workaround for missing None type for vimdicts + windata = {k: windata[k] if windata[k] != '__None__' else None for k in windata.keys()} + powerline = Powerline([ - mksegment(mode, 22, 148, attr=Powerline.ATTR_BOLD), - mksegment(windata['paste'], 231, 166, attr=Powerline.ATTR_BOLD), + mksegment(mode, 22, 148, attr=VimRenderer.ATTR_BOLD), + mksegment(windata['paste'], 231, 166, attr=VimRenderer.ATTR_BOLD), mksegment(windata['branch'], 250, 240, priority=60), mksegment(windata['readonly'], 196, 240, draw_divider=False), mksegment(windata['filepath'], 250, 240, draw_divider=False, priority=40), - mksegment(windata['filename'], windata['filename_color'], 240, attr=Powerline.ATTR_BOLD, draw_divider=False), - mksegment(windata['modified'], 220, 240, attr=Powerline.ATTR_BOLD), + mksegment(windata['filename'], windata['filename_color'], 240, attr=VimRenderer.ATTR_BOLD, draw_divider=False), + mksegment(windata['modified'], 220, 240, attr=VimRenderer.ATTR_BOLD), mksegment(windata['currenttag'], 246, 236, draw_divider=False, priority=100), mksegment(filler=True, cterm_fg=236, cterm_bg=236), mksegment(windata['fileformat'], 247, 236, side='r', priority=50, draw_divider=False), @@ -167,18 +141,18 @@ def statusline(winnr): mksegment(windata['filetype'], 247, 236, side='r', priority=50), mksegment(windata['line_percent'], windata['line_percent_color'], 240, side='r', priority=30), mksegment(u'⭡ ', 239, 252, side='r'), - mksegment(windata['linecurrent'], 235, 252, attr=Powerline.ATTR_BOLD, side='r', draw_divider=False), + mksegment(windata['linecurrent'], 235, 252, attr=VimRenderer.ATTR_BOLD, side='r', draw_divider=False), mksegment(windata['colcurrent'], 244, 252, side='r', priority=30, draw_divider=False), ]) - renderer = VimSegmentRenderer() + renderer = VimRenderer stl = powerline.render(renderer, winwidth) # Replace percent placeholders stl = stl.replace(percent_placeholder, '%%') # Create highlighting groups - for idx, hl in renderer.hl_groups.items(): + for idx, hl in powerline.renderer.hl_groups.items(): if vim_funcs['hlexists'](hl['name']): # Only create hl group if it doesn't already exist continue diff --git a/examples/vim/powerline.vim b/examples/vim/powerline.vim index 4babdcb8..2ef87745 100644 --- a/examples/vim/powerline.vim +++ b/examples/vim/powerline.vim @@ -3,7 +3,7 @@ python import sys, vim, os python sys.path.append(vim.eval('expand(":h:h:h")')) -python from examples.vim.powerline import statusline +python from examples.vim.pl import statusline if exists('*pyeval') let s:pyeval = function('pyeval') From a0b0d15b8f1856fc203fa04e9e49a8030423070f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Thu, 6 Dec 2012 15:55:57 +0100 Subject: [PATCH 10/31] Update default theme --- powerline/themes/vim/default.json | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/powerline/themes/vim/default.json b/powerline/themes/vim/default.json index 17388119..1e204e76 100644 --- a/powerline/themes/vim/default.json +++ b/powerline/themes/vim/default.json @@ -28,7 +28,7 @@ { "type": "function", "name": "file_directory", - "priority" 40, + "priority": 40, "draw_divider": false }, { @@ -50,7 +50,7 @@ { "type": "filler", "highlight": ["background"] - }, + } ], "right": [ { @@ -58,25 +58,27 @@ "name": "file_format", "draw_divider": false, "exclude_modes": ["nc"], - "priority" 50 + "priority": 50 }, { "type": "function", "name": "file_encoding", "exclude_modes": ["nc"], - "priority" 50 + "priority": 50 }, { "type": "function", "name": "file_type", "exclude_modes": ["nc"], - "priority" 50 + "priority": 50 }, { "type": "function", "name": "line_percent", "args": { "gradient": true }, - "priority": 30 + "priority": 30, + "after": "", + "rjust": 4 }, { "type": "string", @@ -86,14 +88,17 @@ { "type": "function", "name": "line_current", - "draw_divider": false + "draw_divider": false, + "rjust": 3 }, { "type": "function", "name": "col_current", "draw_divider": false, - "priority": 30 - }, + "priority": 30, + "before": ":", + "ljust": 3 + } ] } } From 157fff0834357c68fd569af4e285ec0b539cacf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Thu, 6 Dec 2012 17:00:59 +0100 Subject: [PATCH 11/31] Update default colorscheme --- powerline/colorschemes/default.json | 52 +++++++++++++++++++++++++---- 1 file changed, 46 insertions(+), 6 deletions(-) diff --git a/powerline/colorschemes/default.json b/powerline/colorschemes/default.json index 8a792192..08361820 100644 --- a/powerline/colorschemes/default.json +++ b/powerline/colorschemes/default.json @@ -25,9 +25,12 @@ "mediumpurple": 98, "brightpurple": 189, + "mediumorange": 166, "brightorange": 208, "brightestorange": 214, + "brightyellow": 220, + "gray0": 233, "gray1": 235, "gray2": 236, @@ -38,19 +41,56 @@ "gray7": 245, "gray8": 247, "gray9": 250, - "gray10": 252 - } + "gray10": 252, + + "gradient1": 190, + "gradient2": 184, + "gradient3": 178, + "gradient4": 172, + "gradient5": 166, + "gradient6": 160 + }, "groups": { "background": { "fg": "white", "bg": "gray2" }, - "mode_indicator": { "fg": "darkestgreen", "bg": "brightgreen", "attr": ["bold"] } - } + "mode": { "fg": "darkestgreen", "bg": "brightgreen", "attr": ["bold"] }, + "modified_indicator": { "fg": "brightyellow", "bg": "gray4", "attr": ["bold"] }, + "paste_indicator": { "fg": "white", "bg": "mediumorange", "attr": ["bold"] }, + "readonly_indicator": { "fg": "brightestred", "bg": "gray4" }, + "branch": { "fg": "gray9", "bg": "gray4" }, + "file_directory": { "fg": "gray9", "bg": "gray4" }, + "file_name": { "fg": "white", "bg": "gray4", "attr": ["bold"] }, + "file_name_empty": { "fg": "gray9", "bg": "gray4" }, + "file_format": { "fg": "gray8", "bg": "gray2" }, + "file_encoding": { "fg": "gray8", "bg": "gray2" }, + "file_type": { "fg": "gray8", "bg": "gray2" }, + "line_percent": { "fg": "gray9", "bg": "gray4" }, + "line_percent_gradient1": { "fg": "gradient1", "bg": "gray4" }, + "line_percent_gradient2": { "fg": "gradient2", "bg": "gray4" }, + "line_percent_gradient3": { "fg": "gradient3", "bg": "gray4" }, + "line_percent_gradient4": { "fg": "gradient4", "bg": "gray4" }, + "line_percent_gradient5": { "fg": "gradient5", "bg": "gray4" }, + "line_percent_gradient6": { "fg": "gradient6", "bg": "gray4" }, + "line_current": { "fg": "gray1", "bg": "gray10", "attr": ["bold"] }, + "line_current_symbol": { "fg": "gray1", "bg": "gray10" }, + "col_current": { "fg": "gray6", "bg": "gray10" } + }, "mode_translations": { "i": { "colors": { - "gray2": "darkestblue" + "gray0": "darkestblue", + "gray1": "darkestblue", + "gray2": "darkestblue", + "gray3": "darkblue", + "gray4": "darkblue", + "gray5": "darkestcyan", + "gray6": "darkestcyan", + "gray7": "darkestcyan", + "gray8": "mediumcyan", + "gray9": "mediumcyan", + "gray10": "mediumcyan" }, "groups": { - "mode_indicator": { "fg": "darkestcyan", "bg": "white", "attr": ["bold"] } + "mode": { "fg": "darkestcyan", "bg": "white", "attr": ["bold"] } } } } From 3e949adb39872d465ed7303056ca5d48d443fd72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Mon, 10 Dec 2012 13:19:45 +0100 Subject: [PATCH 12/31] Load config files and modules in core --- powerline/colorscheme.py | 5 ++++ powerline/core.py | 53 ++++++++++++++++++++++++++++------- powerline/ext/vim/renderer.py | 4 +-- powerline/renderer.py | 8 +++--- powerline/theme.py | 5 ++++ 5 files changed, 59 insertions(+), 16 deletions(-) diff --git a/powerline/colorscheme.py b/powerline/colorscheme.py index cb345904..62997a91 100644 --- a/powerline/colorscheme.py +++ b/powerline/colorscheme.py @@ -1,5 +1,10 @@ # -*- coding: utf-8 -*- + +class Colorscheme(object): + def __init__(self, colorscheme): + pass + cterm_to_hex = { 16: 0x000000, 17: 0x00005f, 18: 0x000087, 19: 0x0000af, 20: 0x0000d7, 21: 0x0000ff, 22: 0x005f00, 23: 0x005f5f, 24: 0x005f87, 25: 0x005faf, 26: 0x005fd7, 27: 0x005fff, diff --git a/powerline/core.py b/powerline/core.py index 6b7c7d04..db39bdb5 100644 --- a/powerline/core.py +++ b/powerline/core.py @@ -1,17 +1,50 @@ # -*- coding: utf-8 -*- +import importlib +import json +import os +import sys + +from colorscheme import Colorscheme +from theme import Theme + class Powerline(object): - def __init__(self, segments): - '''Create a new Powerline. + def __init__(self, ext): + try: + config_home = os.environ['XDG_CONFIG_HOME'] + except KeyError: + config_home = os.path.expanduser('~/.config') - Segments that aren't filler segments and whose contents aren't None are - dropped from the segment array. - ''' - self.renderer = None # FIXME This should be assigned here based on the current configuration - self.segments = [segment for segment in segments if segment['contents'] is not None or segment['filler']] + config_path = os.path.join(config_home, 'powerline') + plugin_path = os.path.realpath(os.path.dirname(__file__)) + self.search_paths = [config_path, plugin_path] - def render(self, renderer, width=None): - self.renderer = renderer(self.segments) + sys.path[:0] = self.search_paths - return self.renderer.render(width) + # Load main config file, limited to the current extension + self.config = self._load_json_config('config')[ext] + + # Load and initialize colorscheme + colorscheme_config = self._load_json_config(os.path.join('colorschemes', self.config['colorscheme'])) + self.colorscheme = Colorscheme(colorscheme_config) + + # Load and initialize extension theme + theme_config = self._load_json_config(os.path.join('themes', ext, self.config['theme'])) + self.theme = Theme(ext, theme_config) + + # Load and initialize extension renderer + renderer_module_name = 'powerline.ext.{0}.renderer'.format(ext) + renderer_class_name = '{0}Renderer'.format(ext.capitalize()) + renderer_class = getattr(importlib.import_module(renderer_module_name), renderer_class_name) + self.renderer = renderer_class(self.colorscheme, self.theme) + + def _load_json_config(self, config_file): + config_file += '.json' + for path in self.search_paths: + config_file_path = os.path.join(path, config_file) + if os.path.isfile(config_file_path): + with open(config_file_path, 'rb') as config_file_fp: + return json.load(config_file_fp) + + raise IOError('Config file not found in search path: {0}'.format(config_file)) diff --git a/powerline/ext/vim/renderer.py b/powerline/ext/vim/renderer.py index dc64f2d1..2716d466 100644 --- a/powerline/ext/vim/renderer.py +++ b/powerline/ext/vim/renderer.py @@ -6,8 +6,8 @@ from powerline.renderer import Renderer class VimRenderer(Renderer): '''Powerline vim segment renderer. ''' - def __init__(self, segments): - super(VimRenderer, self).__init__(segments) + def __init__(self, colorscheme, theme): + super(VimRenderer, self).__init__(colorscheme, theme) self.hl_groups = {} def hl(self, fg=None, bg=None, attr=None): diff --git a/powerline/renderer.py b/powerline/renderer.py index eb06e135..ec06a6a0 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -19,11 +19,11 @@ class Renderer(object): }, } - def __init__(self, segments): - self.segments = segments - self._hl = {} - def render(self, width=None): + def __init__(self, colorscheme, theme): + pass + + def render(self, mode, width=None): '''Render all the segments with the specified renderer. This method loops through the segment array and compares the diff --git a/powerline/theme.py b/powerline/theme.py index 40a96afc..aad09bdb 100644 --- a/powerline/theme.py +++ b/powerline/theme.py @@ -1 +1,6 @@ # -*- coding: utf-8 -*- + + +class Theme(object): + def __init__(self, ext, theme): + pass From 417f9a6909f96ffb5a2982b043a0d6f2480ef938 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Mon, 10 Dec 2012 14:03:49 +0100 Subject: [PATCH 13/31] Create colorscheme loading class --- powerline/colorscheme.py | 72 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 71 insertions(+), 1 deletion(-) diff --git a/powerline/colorscheme.py b/powerline/colorscheme.py index 62997a91..8324bdd3 100644 --- a/powerline/colorscheme.py +++ b/powerline/colorscheme.py @@ -2,8 +2,78 @@ class Colorscheme(object): + default_mode_key = '__default__' + def __init__(self, colorscheme): - pass + '''Initialize a colorscheme. + ''' + self.colors = {} + self.modes_groups = { + self.default_mode_key: {} + } + + # Create a dict of color tuples with both a cterm and hex value + for color_name, color in colorscheme['colors'].items(): + try: + self.colors[color_name] = (color[0], color[1]) + except TypeError: + self.colors[color_name] = (color, cterm_to_hex[color]) + + # Create highlighting groups for all modes + for group_name, group_props in colorscheme['groups'].items(): + group_attr_flag = self._get_attr_flag(group_props.get('attr', [])) + + self.modes_groups[self.default_mode_key][group_name] = { + 'fg': self.colors[group_props['fg']], + 'bg': self.colors[group_props['bg']], + 'attr': group_attr_flag, + } + + # Create mode-specific highlighting for this group + for mode, translations in colorscheme['mode_translations'].items(): + if not mode in self.modes_groups: + self.modes_groups[mode] = {} + + if group_name in translations['groups']: + # Override entire group if present in the translations group dict + self.modes_groups[mode][group_name] = { + 'fg': self.colors[translations['groups'][group_name]['fg']], + 'bg': self.colors[translations['groups'][group_name]['bg']], + 'attr': self._get_attr_flag(translations['groups'][group_name].get('attr', [])), + } + else: + # Fallback to color translations from the translations colors dict + self.modes_groups[mode][group_name] = { + 'fg': self.colors[translations['colors'].get(group_props['fg'], group_props['fg'])], + 'bg': self.colors[translations['colors'].get(group_props['bg'], group_props['bg'])], + 'attr': group_attr_flag, + } + + def get_highlighting(self, group, mode=None): + '''Return highlighting information for a highlighting group and mode. + + If no mode is specified, or the mode doesn't exist, highlighting for + the default mode is returned. + ''' + if not mode or mode not in self.modes_groups: + mode = self.default_mode_key + + return self.modes_groups[mode][group] + + def _get_attr_flag(self, attributes): + '''Convert an attribute array to a renderer flag. + ''' + from powerline.renderer import Renderer + + attr_flag = 0 + if 'bold' in attributes: + attr_flag |= Renderer.ATTR_BOLD + if 'italic' in attributes: + attr_flag |= Renderer.ATTR_ITALIC + if 'underline' in attributes: + attr_flag |= Renderer.ATTR_UNDERLINE + + return attr_flag cterm_to_hex = { 16: 0x000000, 17: 0x00005f, 18: 0x000087, 19: 0x0000af, 20: 0x0000d7, 21: 0x0000ff, From 039526f720f0166ea250153ef83e396a4861cfdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Mon, 10 Dec 2012 14:23:53 +0100 Subject: [PATCH 14/31] Create theme loading class This class loads all segments from the theme, initializes the highlight group, assigns all necessary properties based on the JSON theme configuration, etc. By doing this we basically move the mksegment() functionality into the theme loader, so this function can be removed at the cost of making it more complicated to use Powerline without its theme functionality. --- powerline/theme.py | 40 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/powerline/theme.py b/powerline/theme.py index aad09bdb..05dfb4f9 100644 --- a/powerline/theme.py +++ b/powerline/theme.py @@ -1,6 +1,44 @@ # -*- coding: utf-8 -*- +import importlib + class Theme(object): def __init__(self, ext, theme): - pass + self.segments = [] + + for side in ['left', 'right']: + for segment in theme['segments'].get(side, []): + contents = None + segment_type = segment.get('type', 'function') + + if segment_type == 'function': + # Import segment function and assign it to the contents + function_module = 'powerline.ext.{0}.segments'.format(ext) + function_name = segment['name'] + contents = getattr(importlib.import_module(function_module), function_name) + elif segment_type == 'string': + contents = segment.get('contents') + elif segment_type == 'filler': + pass + else: + raise TypeError('Unknown segment type: {0}'.format(segment_type)) + + self.segments.append({ + 'type': segment_type, + 'highlight': segment.get('highlight', segment.get('name')), + 'before': segment.get('before'), + 'after': segment.get('after'), + 'contents': contents, + 'args': segment.get('args', {}), + 'ljust': segment.get('ljust', False), + 'rjust': segment.get('rjust', False), + 'priority': segment.get('priority', -1), + 'draw_divider': segment.get('draw_divider', True), + 'side': side, + 'exclude_modes': segment.get('exclude_modes', []), + 'include_modes': segment.get('include_modes', []), + }) + + def get_segments(self): + return self.segments From 87d5db9af77dfb34329d1521b2a41af6a60dc563 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Mon, 10 Dec 2012 14:36:07 +0100 Subject: [PATCH 15/31] Update default configuration --- powerline/config.json | 20 ++++++++++++++++++++ powerline/core.py | 10 ++++++---- powerline/renderer.py | 11 ----------- 3 files changed, 26 insertions(+), 15 deletions(-) create mode 100644 powerline/config.json diff --git a/powerline/config.json b/powerline/config.json new file mode 100644 index 00000000..a570fb6a --- /dev/null +++ b/powerline/config.json @@ -0,0 +1,20 @@ +{ + "common": { + "dividers": { + "left": { + "hard": "⮀", + "soft": "⮁" + }, + "right": { + "hard": "⮂", + "soft": "⮃" + } + } + }, + "ext": { + "vim": { + "colorscheme": "default", + "theme": "default" + } + } +} diff --git a/powerline/core.py b/powerline/core.py index db39bdb5..45283c94 100644 --- a/powerline/core.py +++ b/powerline/core.py @@ -22,15 +22,17 @@ class Powerline(object): sys.path[:0] = self.search_paths - # Load main config file, limited to the current extension - self.config = self._load_json_config('config')[ext] + # Load main config file + config = self._load_json_config('config') + self.config = config['common'] + self.config_ext = config['ext'][ext] # Load and initialize colorscheme - colorscheme_config = self._load_json_config(os.path.join('colorschemes', self.config['colorscheme'])) + colorscheme_config = self._load_json_config(os.path.join('colorschemes', self.config_ext['colorscheme'])) self.colorscheme = Colorscheme(colorscheme_config) # Load and initialize extension theme - theme_config = self._load_json_config(os.path.join('themes', ext, self.config['theme'])) + theme_config = self._load_json_config(os.path.join('themes', ext, self.config_ext['theme'])) self.theme = Theme(ext, theme_config) # Load and initialize extension renderer diff --git a/powerline/renderer.py b/powerline/renderer.py index ec06a6a0..907b6e6f 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -8,17 +8,6 @@ class Renderer(object): ATTR_ITALIC = 2 ATTR_UNDERLINE = 4 - dividers = { - 'l': { - 'hard': u'⮀', - 'soft': u'⮁', - }, - 'r': { - 'hard': u'⮂', - 'soft': u'⮃', - }, - } - def __init__(self, colorscheme, theme): pass From d7ff3f72a6ab62a87c20d5d882fdba5601fcee27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Mon, 10 Dec 2012 14:45:04 +0100 Subject: [PATCH 16/31] Allow theme configuration to override dividers --- powerline/core.py | 2 +- powerline/theme.py | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/powerline/core.py b/powerline/core.py index 45283c94..82d6284c 100644 --- a/powerline/core.py +++ b/powerline/core.py @@ -33,7 +33,7 @@ class Powerline(object): # Load and initialize extension theme theme_config = self._load_json_config(os.path.join('themes', ext, self.config_ext['theme'])) - self.theme = Theme(ext, theme_config) + self.theme = Theme(ext, theme_config, self.config) # Load and initialize extension renderer renderer_module_name = 'powerline.ext.{0}.renderer'.format(ext) diff --git a/powerline/theme.py b/powerline/theme.py index 05dfb4f9..a68f80c3 100644 --- a/powerline/theme.py +++ b/powerline/theme.py @@ -4,11 +4,12 @@ import importlib class Theme(object): - def __init__(self, ext, theme): + def __init__(self, ext, theme_config, common_config): + self.dividers = theme_config.get('dividers', common_config['dividers']) self.segments = [] for side in ['left', 'right']: - for segment in theme['segments'].get(side, []): + for segment in theme_config['segments'].get(side, []): contents = None segment_type = segment.get('type', 'function') @@ -40,5 +41,8 @@ class Theme(object): 'include_modes': segment.get('include_modes', []), }) + def get_divider(self, side='left', type='soft'): + return self.dividers[side][type] + def get_segments(self): return self.segments From 011bacb8c3908de06e578d1a8578e7af0f5834ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Mon, 10 Dec 2012 17:47:07 +0100 Subject: [PATCH 17/31] Make the renderer work with new theme/colorscheme API --- powerline/colorscheme.py | 25 +++++ powerline/core.py | 9 +- powerline/ext/vim/renderer.py | 10 +- powerline/ext/vim/segments/core.py | 22 +++-- powerline/renderer.py | 145 +++++++++++++---------------- powerline/segment.py | 24 ----- powerline/theme.py | 48 ++++++++-- 7 files changed, 160 insertions(+), 123 deletions(-) delete mode 100644 powerline/segment.py diff --git a/powerline/colorscheme.py b/powerline/colorscheme.py index 8324bdd3..7a2edd66 100644 --- a/powerline/colorscheme.py +++ b/powerline/colorscheme.py @@ -49,6 +49,23 @@ class Colorscheme(object): 'attr': group_attr_flag, } + def get_group_highlighting(self, group): + '''Return highlighting information for all modes of a highlighting group. + ''' + group_highlighting = {} + for mode, mode_group in self.modes_groups.items(): + try: + group_highlighting[mode] = mode_group[group] + except TypeError: + for try_group in group: + if try_group in self.modes_groups[mode]: + group_highlighting[mode] = mode_group[try_group] + break + finally: + if mode not in group_highlighting: + raise KeyError('Highlighting groups not found in colorscheme: {0}'.format(group)) + return group_highlighting + def get_highlighting(self, group, mode=None): '''Return highlighting information for a highlighting group and mode. @@ -58,6 +75,14 @@ class Colorscheme(object): if not mode or mode not in self.modes_groups: mode = self.default_mode_key + try: + return self.modes_groups[mode][group] + except TypeError: + for try_group in group: + if try_group in self.modes_groups[mode]: + return self.modes_groups[mode][try_group] + raise KeyError('Highlighting groups not found in colorscheme: {0}'.format(group)) + return self.modes_groups[mode][group] def _get_attr_flag(self, attributes): diff --git a/powerline/core.py b/powerline/core.py index 82d6284c..785d86ba 100644 --- a/powerline/core.py +++ b/powerline/core.py @@ -29,17 +29,17 @@ class Powerline(object): # Load and initialize colorscheme colorscheme_config = self._load_json_config(os.path.join('colorschemes', self.config_ext['colorscheme'])) - self.colorscheme = Colorscheme(colorscheme_config) + colorscheme = Colorscheme(colorscheme_config) # Load and initialize extension theme theme_config = self._load_json_config(os.path.join('themes', ext, self.config_ext['theme'])) - self.theme = Theme(ext, theme_config, self.config) + self.theme = Theme(ext, colorscheme, theme_config, self.config) # Load and initialize extension renderer renderer_module_name = 'powerline.ext.{0}.renderer'.format(ext) renderer_class_name = '{0}Renderer'.format(ext.capitalize()) renderer_class = getattr(importlib.import_module(renderer_module_name), renderer_class_name) - self.renderer = renderer_class(self.colorscheme, self.theme) + self.renderer = renderer_class(self.theme) def _load_json_config(self, config_file): config_file += '.json' @@ -50,3 +50,6 @@ class Powerline(object): return json.load(config_file_fp) raise IOError('Config file not found in search path: {0}'.format(config_file)) + + def render(self, mode, width=None): + return self.renderer.render(mode, width) diff --git a/powerline/ext/vim/renderer.py b/powerline/ext/vim/renderer.py index 2716d466..2c202a72 100644 --- a/powerline/ext/vim/renderer.py +++ b/powerline/ext/vim/renderer.py @@ -6,8 +6,8 @@ from powerline.renderer import Renderer class VimRenderer(Renderer): '''Powerline vim segment renderer. ''' - def __init__(self, colorscheme, theme): - super(VimRenderer, self).__init__(colorscheme, theme) + def __init__(self, theme): + super(VimRenderer, self).__init__(theme) self.hl_groups = {} def hl(self, fg=None, bg=None, attr=None): @@ -41,11 +41,11 @@ class VimRenderer(Renderer): if attr: hl_group['attr'] = [] - if attr & Renderer.ATTR_BOLD: + if attr & self.ATTR_BOLD: hl_group['attr'].append('bold') - if attr & Renderer.ATTR_ITALIC: + if attr & self.ATTR_ITALIC: hl_group['attr'].append('italic') - if attr & Renderer.ATTR_UNDERLINE: + if attr & self.ATTR_UNDERLINE: hl_group['attr'].append('underline') hl_group['name'] = 'Pl_' + \ diff --git a/powerline/ext/vim/segments/core.py b/powerline/ext/vim/segments/core.py index a08d1443..c4750166 100644 --- a/powerline/ext/vim/segments/core.py +++ b/powerline/ext/vim/segments/core.py @@ -1,8 +1,9 @@ # -*- coding: utf-8 -*- +import os import vim -from bindings import vim_get_func +from powerline.ext.vim.bindings import vim_get_func vim_funcs = { 'col': vim_get_func('col', rettype=int), @@ -48,12 +49,12 @@ def mode(override=None): mode = vim_funcs['mode']() if not override: - return (mode, vim_modes[mode]) + return vim_modes[mode] try: - return (mode, override[mode]) + return override[mode] except IndexError: - return (mode, vim_modes[mode]) + return vim_modes[mode] def modified_indicator(text=u'+'): @@ -93,7 +94,7 @@ def branch(): def file_directory(): '''Return file directory (head component of the file path). ''' - return vim_funcs['expand']('%:~:.:h') + return vim_funcs['expand']('%:~:.:h') + os.sep def file_name(): @@ -126,13 +127,20 @@ def file_type(): return vim.eval('&filetype') or None -def line_percent(): +def line_percent(gradient=False): '''Return the cursor position in the file as a percentage. ''' line_current = vim_funcs['line']('.') line_last = vim_funcs['line']('$') + percentage = int(line_current * 100 // line_last) - return line_current * 100 // line_last + if not gradient: + return percentage + + return { + 'contents': percentage, + 'highlight': 'line_percent_gradient' + str(int(5 * percentage // 100) + 1), + } def line_current(): diff --git a/powerline/renderer.py b/powerline/renderer.py index 907b6e6f..28a5be4d 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -1,23 +1,17 @@ # -*- coding: utf-8 -*- -from segment import mksegment - class Renderer(object): ATTR_BOLD = 1 ATTR_ITALIC = 2 ATTR_UNDERLINE = 4 - - def __init__(self, colorscheme, theme): - pass + def __init__(self, theme): + self.segments = [] + self.theme = theme def render(self, mode, 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. + '''Render all segments. 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 @@ -25,71 +19,8 @@ class Renderer(object): 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) + self.segments = self.theme.get_segments() + rendered_highlighted = self._render_segments(mode) if not width: # No width specified, so we don't need to crop or pad anything @@ -103,10 +34,10 @@ class Renderer(object): segments_priority.pop(0) # Do another render pass so we can calculate the correct amount of filler space - render_segments(self.segments) + self._render_segments(mode, False) # Distribute the remaining space on the filler segments - segments_fillers = [segment for segment in self.segments if segment['filler'] is True] + segments_fillers = [segment for segment in self.segments if segment['type'] == 'filler'] if segments_fillers: segments_fillers_len, segments_fillers_remainder = divmod((width - self._total_len()), len(segments_fillers)) segments_fillers_contents = ' ' * segments_fillers_len @@ -115,7 +46,65 @@ class Renderer(object): # Add remainder whitespace to the first filler segment segments_fillers[0]['contents'] += ' ' * segments_fillers_remainder - return render_segments(self.segments) + return self._render_segments(mode) + + def _render_segments(self, mode, render_highlighted=True): + '''Internal segment rendering method. + + This method loops through the segment array and compares the + foreground/background colors and divider properties and returns the + rendered statusline as a string. + + The method always renders the raw segment contents (i.e. without + highlighting strings added), and only renders the highlighted + statusline if render_highlighted is True. + ''' + rendered_highlighted = u'' + segments_len = len(self.segments) + + for index, segment in enumerate(self.segments): + prev_segment = self.segments[index - 1] if index > 0 else None + next_segment = self.segments[index + 1] if index < segments_len - 1 else None + compare_segment = next_segment if segment['side'] == 'left' else prev_segment + + segment['rendered_raw'] = u'' + outer_padding = ' ' if index == 0 or index == segments_len - 1 else '' + divider_type = 'soft' if compare_segment['highlight'][mode]['bg'] == segment['highlight'][mode]['bg'] else 'hard' + divider = self.theme.get_divider(segment['side'], divider_type) + + divider_hl = '' + segment_hl = '' + + if render_highlighted: + if divider_type == 'hard': + divider_hl = self.hl(segment['highlight'][mode]['bg'], compare_segment['highlight'][mode]['bg'], False) + + segment_hl = self.hl(**segment['highlight'][mode]) + + if segment['type'] == 'filler': + rendered_highlighted += segment['contents'] or '' + 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'] == 'left': + 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'] == 'left': + 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: + raise ValueError('Unknown segment type') + + return rendered_highlighted def _total_len(self): '''Return total/rendered length of all segments. diff --git a/powerline/segment.py b/powerline/segment.py deleted file mode 100644 index 512ea574..00000000 --- a/powerline/segment.py +++ /dev/null @@ -1,24 +0,0 @@ -# -*- coding: utf-8 -*- - -from colorscheme import cterm_to_hex - - -def mksegment(contents=None, cterm_fg=False, cterm_bg=False, attr=False, hex_fg=False, hex_bg=False, side='l', draw_divider=True, priority=-1, filler=False): - '''Convenience wrapper for segment generation. - ''' - if contents is not None or filler: - try: - contents = unicode(contents or u'') - except UnicodeDecodeError: - contents = contents.decode('utf-8') or u'' - - return { - 'contents': contents, - 'fg': (cterm_fg, hex_fg or cterm_to_hex.get(cterm_fg, 0xffffff)), - 'bg': (cterm_bg, hex_bg or cterm_to_hex.get(cterm_bg, 0x000000)), - 'attr': attr, - 'side': side, - 'draw_divider': False if filler else draw_divider, - 'priority': priority, - 'filler': filler, - } diff --git a/powerline/theme.py b/powerline/theme.py index a68f80c3..dfee4321 100644 --- a/powerline/theme.py +++ b/powerline/theme.py @@ -4,20 +4,22 @@ import importlib class Theme(object): - def __init__(self, ext, theme_config, common_config): + def __init__(self, ext, colorscheme, theme_config, common_config): + self.colorscheme = colorscheme self.dividers = theme_config.get('dividers', common_config['dividers']) self.segments = [] for side in ['left', 'right']: for segment in theme_config['segments'].get(side, []): contents = None + contents_func = None segment_type = segment.get('type', 'function') if segment_type == 'function': # Import segment function and assign it to the contents function_module = 'powerline.ext.{0}.segments'.format(ext) function_name = segment['name'] - contents = getattr(importlib.import_module(function_module), function_name) + contents_func = getattr(importlib.import_module(function_module), function_name) elif segment_type == 'string': contents = segment.get('contents') elif segment_type == 'filler': @@ -25,11 +27,14 @@ class Theme(object): else: raise TypeError('Unknown segment type: {0}'.format(segment_type)) + highlighting_group = segment.get('highlight', segment.get('name')) + self.segments.append({ 'type': segment_type, - 'highlight': segment.get('highlight', segment.get('name')), - 'before': segment.get('before'), - 'after': segment.get('after'), + 'highlight': self.colorscheme.get_group_highlighting(highlighting_group), + 'before': segment.get('before', ''), + 'after': segment.get('after', ''), + 'contents_func': contents_func, 'contents': contents, 'args': segment.get('args', {}), 'ljust': segment.get('ljust', False), @@ -42,7 +47,38 @@ class Theme(object): }) def get_divider(self, side='left', type='soft'): + '''Return segment divider. + ''' return self.dividers[side][type] def get_segments(self): - return self.segments + '''Return all segments. + + Function segments are called, and all segments get their before/after + and ljust/rjust properties applied. + ''' + return_segments = [] + for segment in self.segments: + if segment['type'] == 'function': + contents_func_ret = segment['contents_func'](**segment['args']) + + if contents_func_ret is None: + continue + + try: + segment['highlight'] = self.colorscheme.get_group_highlighting(contents_func_ret['highlight']) + segment['contents'] = contents_func_ret['contents'] + except TypeError: + segment['contents'] = contents_func_ret + elif segment['type'] == 'filler' or (segment['type'] == 'string' and segment['contents'] is not None): + pass + else: + continue + + segment['contents'] = unicode(segment['before'] + unicode(segment['contents']) + segment['after'])\ + .ljust(segment['ljust'])\ + .rjust(segment['rjust']) + + return_segments.append(segment) + + return return_segments From 19248503ef874ea85f4ebb00391b56193f527c4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Mon, 10 Dec 2012 18:04:18 +0100 Subject: [PATCH 18/31] Improve default colorscheme with more hl groups --- powerline/colorschemes/default.json | 31 ++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/powerline/colorschemes/default.json b/powerline/colorschemes/default.json index 08361820..bdcf2605 100644 --- a/powerline/colorschemes/default.json +++ b/powerline/colorschemes/default.json @@ -75,6 +75,29 @@ "col_current": { "fg": "gray6", "bg": "gray10" } }, "mode_translations": { + "nc": { + "colors": { + "gray0": "gray0", + "gray1": "gray0", + "gray2": "gray0", + "gray3": "gray1", + "gray4": "gray1", + "gray5": "gray1", + "gray6": "gray1", + "gray7": "gray4", + "gray8": "gray4", + "gray9": "gray4", + "gray10": "gray5", + "white": "gray6", + "gradient1": "gray5", + "gradient2": "gray5", + "gradient3": "gray5", + "gradient4": "gray5", + "gradient5": "gray5", + "gradient6": "gray5" + }, + "groups": {} + }, "i": { "colors": { "gray0": "darkestblue", @@ -87,7 +110,13 @@ "gray7": "darkestcyan", "gray8": "mediumcyan", "gray9": "mediumcyan", - "gray10": "mediumcyan" + "gray10": "mediumcyan", + "gradient1": "mediumcyan", + "gradient2": "mediumcyan", + "gradient3": "mediumcyan", + "gradient4": "mediumcyan", + "gradient5": "mediumcyan", + "gradient6": "mediumcyan" }, "groups": { "mode": { "fg": "darkestcyan", "bg": "white", "attr": ["bold"] } From 42d8353db6774cf403a944cc2348be5b82e66e26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Mon, 10 Dec 2012 19:46:54 +0100 Subject: [PATCH 19/31] Handle segment exclude/include modes --- powerline/renderer.py | 2 +- powerline/theme.py | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/powerline/renderer.py b/powerline/renderer.py index 28a5be4d..8b71abf3 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -19,7 +19,7 @@ class Renderer(object): provided they will fill the remaining space until the desired width is reached. ''' - self.segments = self.theme.get_segments() + self.segments = self.theme.get_segments(mode) rendered_highlighted = self._render_segments(mode) if not width: diff --git a/powerline/theme.py b/powerline/theme.py index dfee4321..8adb9b7f 100644 --- a/powerline/theme.py +++ b/powerline/theme.py @@ -51,7 +51,7 @@ class Theme(object): ''' return self.dividers[side][type] - def get_segments(self): + def get_segments(self, mode): '''Return all segments. Function segments are called, and all segments get their before/after @@ -59,6 +59,9 @@ class Theme(object): ''' return_segments = [] for segment in self.segments: + if mode in segment['exclude_modes'] or (segment['include_modes'] and segment not in segment['include_modes']): + continue + if segment['type'] == 'function': contents_func_ret = segment['contents_func'](**segment['args']) From 90f8f94468c58fe14ed4cca2e601b96921c41dee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Mon, 10 Dec 2012 20:00:26 +0100 Subject: [PATCH 20/31] Update vim statusline example A major issue is that we currently can't pass any windows' mode on to the segment rendering method, so non-current windows don't get highlighted correctly, and segments don't get removed if they have 'nc' in their exclude_modes setting, and statuslines in non-current windows don't get resized until the window is focused again. --- examples/vim/pl.py | 161 +++++-------------------------------- examples/vim/powerline.vim | 2 +- 2 files changed, 23 insertions(+), 140 deletions(-) diff --git a/examples/vim/pl.py b/examples/vim/pl.py index 2ef16e03..d356d003 100644 --- a/examples/vim/pl.py +++ b/examples/vim/pl.py @@ -1,160 +1,43 @@ # -*- coding: utf-8 -*- import vim -import os - from powerline.core import Powerline -from powerline.segment import mksegment -from powerline.ext.vim import VimRenderer from powerline.ext.vim.bindings import vim_get_func -modes = { - 'n': 'NORMAL', - 'no': 'N·OPER', - 'v': 'VISUAL', - 'V': 'V·LINE', - '': 'V·BLCK', - 's': 'SELECT', - 'S': 'S·LINE', - '': 'S·BLCK', - 'i': 'INSERT', - 'R': 'REPLACE', - 'Rv': 'V·RPLCE', - 'c': 'COMMND', - 'cv': 'VIM EX', - 'ce': 'EX', - 'r': 'PROMPT', - 'rm': 'MORE', - 'r?': 'CONFIRM', - '!': 'SHELL', -} +vim_winwidth = vim_get_func('winwidth', rettype=int) +vim_hlexists = vim_get_func('hlexists', rettype=int) +vim_getwinvar = vim_get_func('getwinvar') +vim_setwinvar = vim_get_func('setwinvar') -# We need to replace this private use glyph with a double-percent later +created_hl_groups = [] percent_placeholder = u'' -vim_funcs = { - 'winwidth': vim_get_func('winwidth', rettype=int), - 'mode': vim_get_func('mode'), - 'fghead': vim_get_func('fugitive#head'), - 'line': vim_get_func('line', rettype=int), - 'col': vim_get_func('col', rettype=int), - 'expand': vim_get_func('expand'), - 'tbcurtag': vim_get_func('tagbar#currenttag'), - 'hlexists': vim_get_func('hlexists', rettype=int), -} - -getwinvar = vim_get_func('getwinvar') -setwinvar = vim_get_func('setwinvar') +pl = Powerline('vim') def statusline(winnr): - winwidth = vim_funcs['winwidth'](winnr) + current = vim_getwinvar(winnr, 'current') + windata = vim_getwinvar(winnr, 'powerline') + winwidth = vim_winwidth(winnr) - current = getwinvar(winnr, 'current') - windata = getwinvar(winnr, 'powerline') + if current or not windata: + mode = vim_get_func('mode')() + if mode == 'n': + mode = '__default__' - if current or not windata.keys(): - # Recreate segment data for each redraw if we're in the current window - line_current = vim_funcs['line']('.') - line_end = vim_funcs['line']('$') - line_percent = line_current * 100 // line_end + stl = pl.render(mode, winwidth) - try: - branch = vim_funcs['fghead'](5) - except vim.error: - vim_funcs['fghead'] = None - branch = '' - except TypeError: - branch = '' - if branch: - branch = u'⭠ ' + branch - - # Fun gradient colored percent segment - line_percent_gradient = [160, 166, 172, 178, 184, 190] - line_percent_color = line_percent_gradient[int((len(line_percent_gradient) - 1) * line_percent / 100)] - - col_current = vim_funcs['col']('.') - - filepath, filename = os.path.split(vim_funcs['expand']('%:~:.')) - filename_color = 231 - if filepath: - filepath += os.sep - - if not filename: - filename = '[No Name]' - filename_color = 250 - - readonly = vim.eval('&ro ? "⭤ " : ""') - modified = vim.eval('&mod ? " +" : ""') - - try: - currenttag = vim_funcs['tbcurtag']('%s', '') - except vim.error: - vim_funcs['tbcurtag'] = None - currenttag = '' - except TypeError: - currenttag = '' - - windata = { - 'paste': vim.eval('&paste ? "PASTE" : ""') or None, - 'branch': branch, - 'readonly': readonly, - 'filepath': filepath, - 'filename': filename, - 'filename_color': filename_color, - 'modified': modified, - 'currenttag': currenttag, - 'fileformat': vim.eval('&ff'), - 'fileencoding': vim.eval('&fenc'), - 'filetype': vim.eval('&ft'), - 'line_percent': str(line_percent).rjust(3) + percent_placeholder, - 'line_percent_color': line_percent_color, - 'linecurrent': str(line_current).rjust(3), - 'colcurrent': ':' + str(col_current).ljust(2), - } - - # Horrible workaround for missing None type for vimdicts - windata = {k: v if v is not None else '__None__' for k, v in windata.items()} - - setwinvar(winnr, 'powerline', windata) - - mode = modes[vim_funcs['mode']()] - - if not current: - mode = None - - # Horrible workaround for missing None type for vimdicts - windata = {k: windata[k] if windata[k] != '__None__' else None for k in windata.keys()} - - powerline = Powerline([ - mksegment(mode, 22, 148, attr=VimRenderer.ATTR_BOLD), - mksegment(windata['paste'], 231, 166, attr=VimRenderer.ATTR_BOLD), - mksegment(windata['branch'], 250, 240, priority=60), - mksegment(windata['readonly'], 196, 240, draw_divider=False), - mksegment(windata['filepath'], 250, 240, draw_divider=False, priority=40), - mksegment(windata['filename'], windata['filename_color'], 240, attr=VimRenderer.ATTR_BOLD, draw_divider=False), - mksegment(windata['modified'], 220, 240, attr=VimRenderer.ATTR_BOLD), - mksegment(windata['currenttag'], 246, 236, draw_divider=False, priority=100), - mksegment(filler=True, cterm_fg=236, cterm_bg=236), - mksegment(windata['fileformat'], 247, 236, side='r', priority=50, draw_divider=False), - mksegment(windata['fileencoding'], 247, 236, side='r', priority=50), - mksegment(windata['filetype'], 247, 236, side='r', priority=50), - mksegment(windata['line_percent'], windata['line_percent_color'], 240, side='r', priority=30), - mksegment(u'⭡ ', 239, 252, side='r'), - mksegment(windata['linecurrent'], 235, 252, attr=VimRenderer.ATTR_BOLD, side='r', draw_divider=False), - mksegment(windata['colcurrent'], 244, 252, side='r', priority=30, draw_divider=False), - ]) - - renderer = VimRenderer - stl = powerline.render(renderer, winwidth) + vim_setwinvar(winnr, 'powerline', stl) + else: + mode = 'nc' + stl = vim_getwinvar(winnr, 'powerline').decode('utf-8') # Replace percent placeholders stl = stl.replace(percent_placeholder, '%%') # Create highlighting groups - for idx, hl in powerline.renderer.hl_groups.items(): - if vim_funcs['hlexists'](hl['name']): - # Only create hl group if it doesn't already exist + for hl in pl.renderer.hl_groups.values(): + if hl['name'] in created_hl_groups: continue vim.command('hi {group} ctermfg={ctermfg} guifg={guifg} guibg={guibg} ctermbg={ctermbg} cterm={attr} gui={attr}'.format( @@ -166,6 +49,6 @@ def statusline(winnr): attr=','.join(hl['attr']), )) - return stl + created_hl_groups.append(hl['name']) -# vim: ft=python ts=4 sts=4 sw=4 noet + return stl diff --git a/examples/vim/powerline.vim b/examples/vim/powerline.vim index 2ef87745..d709dac5 100644 --- a/examples/vim/powerline.vim +++ b/examples/vim/powerline.vim @@ -20,7 +20,7 @@ endfunction function! s:WinDoPowerline() if ! exists('w:powerline') - let w:powerline = {} + let w:powerline = '' endif let &l:stl = '%!Powerline('. winnr() .')' From 240bd6217df357b52d26867726b0bed2fc63b0d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Mon, 10 Dec 2012 20:17:42 +0100 Subject: [PATCH 21/31] Remove powerline render() method --- examples/vim/pl.py | 2 +- powerline/core.py | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/examples/vim/pl.py b/examples/vim/pl.py index d356d003..2c032d2c 100644 --- a/examples/vim/pl.py +++ b/examples/vim/pl.py @@ -25,7 +25,7 @@ def statusline(winnr): if mode == 'n': mode = '__default__' - stl = pl.render(mode, winwidth) + stl = pl.renderer.render(mode, winwidth) vim_setwinvar(winnr, 'powerline', stl) else: diff --git a/powerline/core.py b/powerline/core.py index 785d86ba..85dd36d1 100644 --- a/powerline/core.py +++ b/powerline/core.py @@ -50,6 +50,3 @@ class Powerline(object): return json.load(config_file_fp) raise IOError('Config file not found in search path: {0}'.format(config_file)) - - def render(self, mode, width=None): - return self.renderer.render(mode, width) From ec278943b1c3ff760eba5a252646575a7cd15e09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Mon, 10 Dec 2012 20:18:02 +0100 Subject: [PATCH 22/31] Fix Unicode quirks with mode segment --- powerline/ext/vim/segments/core.py | 36 +++++++++++++++--------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/powerline/ext/vim/segments/core.py b/powerline/ext/vim/segments/core.py index c4750166..5de784b8 100644 --- a/powerline/ext/vim/segments/core.py +++ b/powerline/ext/vim/segments/core.py @@ -16,24 +16,24 @@ vim_funcs = { } vim_modes = { - 'n': 'NORMAL', - 'no': 'N·OPER', - 'v': 'VISUAL', - 'V': 'V·LINE', - '': 'V·BLCK', - 's': 'SELECT', - 'S': 'S·LINE', - '': 'S·BLCK', - 'i': 'INSERT', - 'R': 'REPLACE', - 'Rv': 'V·RPLCE', - 'c': 'COMMND', - 'cv': 'VIM EX', - 'ce': 'EX', - 'r': 'PROMPT', - 'rm': 'MORE', - 'r?': 'CONFIRM', - '!': 'SHELL', + 'n': u'NORMAL', + 'no': u'N·OPER', + 'v': u'VISUAL', + 'V': u'V·LINE', + '': u'V·BLCK', + 's': u'SELECT', + 'S': u'S·LINE', + '': u'S·BLCK', + 'i': u'INSERT', + 'R': u'REPLACE', + 'Rv': u'V·RPLCE', + 'c': u'COMMND', + 'cv': u'VIM EX', + 'ce': u'EX', + 'r': u'PROMPT', + 'rm': u'MORE', + 'r?': u'CONFIRM', + '!': u'SHELL', } From e22a9241d049359a077f4c0add5236bd262e2984 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Mon, 10 Dec 2012 20:18:19 +0100 Subject: [PATCH 23/31] Fallback to default if mode highlighting is missing --- powerline/renderer.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/powerline/renderer.py b/powerline/renderer.py index 8b71abf3..ff31e8c2 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -1,5 +1,7 @@ # -*- coding: utf-8 -*- +from colorscheme import Colorscheme + class Renderer(object): ATTR_BOLD = 1 @@ -61,6 +63,7 @@ class Renderer(object): ''' rendered_highlighted = u'' segments_len = len(self.segments) + mode = mode if mode in self.segments[0]['highlight'] else Colorscheme.default_mode_key for index, segment in enumerate(self.segments): prev_segment = self.segments[index - 1] if index > 0 else None From 32f689318920ac951590de4e79a16b801c5dbf9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Wed, 12 Dec 2012 12:36:27 +0100 Subject: [PATCH 24/31] Update vim renderer to handle all vim-specific stuff --- powerline/ext/vim/renderer.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/powerline/ext/vim/renderer.py b/powerline/ext/vim/renderer.py index 2c202a72..104581d6 100644 --- a/powerline/ext/vim/renderer.py +++ b/powerline/ext/vim/renderer.py @@ -2,14 +2,23 @@ from powerline.renderer import Renderer +import vim + class VimRenderer(Renderer): '''Powerline vim segment renderer. ''' + PERCENT_PLACEHOLDER = u'' + def __init__(self, theme): super(VimRenderer, self).__init__(theme) self.hl_groups = {} + def render(self, mode, width=None): + statusline = super(VimRenderer, self).render(mode, width) + statusline = statusline.replace(self.PERCENT_PLACEHOLDER, '%%') + return statusline + def hl(self, fg=None, bg=None, attr=None): '''Highlight a segment. @@ -57,4 +66,14 @@ class VimRenderer(Renderer): self.hl_groups[(fg, bg, attr)] = hl_group + # Create highlighting group in vim + vim.command('hi {group} ctermfg={ctermfg} guifg={guifg} guibg={guibg} ctermbg={ctermbg} cterm={attr} gui={attr}'.format( + group=hl_group['name'], + ctermfg=hl_group['ctermfg'], + guifg='#{0:06x}'.format(hl_group['guifg']) if hl_group['guifg'] != 'NONE' else 'NONE', + ctermbg=hl_group['ctermbg'], + guibg='#{0:06x}'.format(hl_group['guibg']) if hl_group['guibg'] != 'NONE' else 'NONE', + attr=','.join(hl_group['attr']), + )) + return '%#' + self.hl_groups[(fg, bg, attr)]['name'] + '#' From c6ac449af1b77317a6e1ea7f151361eb3734969b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Wed, 12 Dec 2012 12:36:39 +0100 Subject: [PATCH 25/31] Fix minor issues in default theme --- powerline/themes/vim/default.json | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/powerline/themes/vim/default.json b/powerline/themes/vim/default.json index 1e204e76..65fb96db 100644 --- a/powerline/themes/vim/default.json +++ b/powerline/themes/vim/default.json @@ -4,7 +4,8 @@ "left": [ { "type": "function", - "name": "mode" + "name": "mode", + "exclude_modes": ["nc"] }, { "type": "function", @@ -36,16 +37,12 @@ "name": "file_name", "draw_divider": false }, - { - "type": "function", - "name": "modified_indicator", - "draw_divider": false - }, { "type": "function", "name": "modified_indicator", "args": { "text": "+" }, - "exclude_modes": ["nc"] + "exclude_modes": ["nc"], + "before": " " }, { "type": "filler", From 8960d15cf5bff23cac0c9c0d1a7408adcc01dde4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Wed, 12 Dec 2012 12:37:58 +0100 Subject: [PATCH 26/31] Update vim statusline example The statusline example mostly works now, even with different modes. The main problem is still non-current windows, which receive the contents of the currently active window for most segments. --- examples/vim/pl.py | 39 ++++----------------------------------- 1 file changed, 4 insertions(+), 35 deletions(-) diff --git a/examples/vim/pl.py b/examples/vim/pl.py index 2c032d2c..015e3994 100644 --- a/examples/vim/pl.py +++ b/examples/vim/pl.py @@ -1,54 +1,23 @@ # -*- coding: utf-8 -*- -import vim from powerline.core import Powerline from powerline.ext.vim.bindings import vim_get_func vim_winwidth = vim_get_func('winwidth', rettype=int) -vim_hlexists = vim_get_func('hlexists', rettype=int) vim_getwinvar = vim_get_func('getwinvar') vim_setwinvar = vim_get_func('setwinvar') -created_hl_groups = [] -percent_placeholder = u'' - pl = Powerline('vim') def statusline(winnr): current = vim_getwinvar(winnr, 'current') - windata = vim_getwinvar(winnr, 'powerline') winwidth = vim_winwidth(winnr) - if current or not windata: - mode = vim_get_func('mode')() - if mode == 'n': - mode = '__default__' - - stl = pl.renderer.render(mode, winwidth) - - vim_setwinvar(winnr, 'powerline', stl) - else: + mode = vim_get_func('mode')() + if not current: mode = 'nc' - stl = vim_getwinvar(winnr, 'powerline').decode('utf-8') - # Replace percent placeholders - stl = stl.replace(percent_placeholder, '%%') + statusline = pl.renderer.render(mode, winwidth) - # Create highlighting groups - for hl in pl.renderer.hl_groups.values(): - if hl['name'] in created_hl_groups: - continue - - vim.command('hi {group} ctermfg={ctermfg} guifg={guifg} guibg={guibg} ctermbg={ctermbg} cterm={attr} gui={attr}'.format( - group=hl['name'], - ctermfg=hl['ctermfg'], - guifg='#{0:06x}'.format(hl['guifg']) if hl['guifg'] != 'NONE' else 'NONE', - ctermbg=hl['ctermbg'], - guibg='#{0:06x}'.format(hl['guibg']) if hl['guibg'] != 'NONE' else 'NONE', - attr=','.join(hl['attr']), - )) - - created_hl_groups.append(hl['name']) - - return stl + return statusline From 04993264e4cb383ddee7e131a7320f211fbeb14c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Wed, 12 Dec 2012 18:15:21 +0100 Subject: [PATCH 27/31] Move more vim stuff into the renderer --- examples/vim/pl.py | 23 ----------------------- examples/vim/powerline.vim | 4 +++- powerline/ext/vim/renderer.py | 18 ++++++++++++++++-- 3 files changed, 19 insertions(+), 26 deletions(-) delete mode 100644 examples/vim/pl.py diff --git a/examples/vim/pl.py b/examples/vim/pl.py deleted file mode 100644 index 015e3994..00000000 --- a/examples/vim/pl.py +++ /dev/null @@ -1,23 +0,0 @@ -# -*- coding: utf-8 -*- - -from powerline.core import Powerline -from powerline.ext.vim.bindings import vim_get_func - -vim_winwidth = vim_get_func('winwidth', rettype=int) -vim_getwinvar = vim_get_func('getwinvar') -vim_setwinvar = vim_get_func('setwinvar') - -pl = Powerline('vim') - - -def statusline(winnr): - current = vim_getwinvar(winnr, 'current') - winwidth = vim_winwidth(winnr) - - mode = vim_get_func('mode')() - if not current: - mode = 'nc' - - statusline = pl.renderer.render(mode, winwidth) - - return statusline diff --git a/examples/vim/powerline.vim b/examples/vim/powerline.vim index d709dac5..33bba76a 100644 --- a/examples/vim/powerline.vim +++ b/examples/vim/powerline.vim @@ -4,6 +4,8 @@ python import sys, vim, os python sys.path.append(vim.eval('expand(":h:h:h")')) python from examples.vim.pl import statusline +python from powerline.core import Powerline +python pl = Powerline('vim') if exists('*pyeval') let s:pyeval = function('pyeval') @@ -15,7 +17,7 @@ else endif function! Powerline(winnr) - return s:pyeval('statusline('. a:winnr .')') + return s:pyeval('pl.renderer.render('. a:winnr .')') endfunction function! s:WinDoPowerline() diff --git a/powerline/ext/vim/renderer.py b/powerline/ext/vim/renderer.py index 104581d6..59dcadef 100644 --- a/powerline/ext/vim/renderer.py +++ b/powerline/ext/vim/renderer.py @@ -1,9 +1,15 @@ # -*- coding: utf-8 -*- +from powerline.ext.vim.bindings import vim_get_func from powerline.renderer import Renderer import vim +vim_mode = vim_get_func('mode') +vim_winwidth = vim_get_func('winwidth', rettype=int) +vim_getwinvar = vim_get_func('getwinvar') +vim_setwinvar = vim_get_func('setwinvar') + class VimRenderer(Renderer): '''Powerline vim segment renderer. @@ -14,9 +20,17 @@ class VimRenderer(Renderer): super(VimRenderer, self).__init__(theme) self.hl_groups = {} - def render(self, mode, width=None): - statusline = super(VimRenderer, self).render(mode, width) + def render(self, winnr): + current = vim_getwinvar(winnr, 'current') + winwidth = vim_winwidth(winnr) + + mode = vim_mode() + if not current: + mode = 'nc' + + statusline = super(VimRenderer, self).render(mode, winwidth) statusline = statusline.replace(self.PERCENT_PLACEHOLDER, '%%') + return statusline def hl(self, fg=None, bg=None, attr=None): From c2dfabdb8dbac7c6a9a24a3b188f4793bb1db71d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Thu, 13 Dec 2012 12:49:15 +0100 Subject: [PATCH 28/31] Load segments from specific segment modules --- powerline/ext/vim/segments/__init__.py | 5 ----- powerline/theme.py | 3 ++- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/powerline/ext/vim/segments/__init__.py b/powerline/ext/vim/segments/__init__.py index cf7d729a..e69de29b 100644 --- a/powerline/ext/vim/segments/__init__.py +++ b/powerline/ext/vim/segments/__init__.py @@ -1,5 +0,0 @@ -# flake8: noqa - -from core import (mode, modified_indicator, paste_indicator, - readonly_indicator, branch, file_directory, file_name, file_format, - file_encoding, file_type, line_percent, line_current, col_current) diff --git a/powerline/theme.py b/powerline/theme.py index 8adb9b7f..2c69c6ff 100644 --- a/powerline/theme.py +++ b/powerline/theme.py @@ -14,10 +14,11 @@ class Theme(object): contents = None contents_func = None segment_type = segment.get('type', 'function') + segment_module = segment.get('module', 'core') if segment_type == 'function': # Import segment function and assign it to the contents - function_module = 'powerline.ext.{0}.segments'.format(ext) + function_module = 'powerline.ext.{0}.segments.{1}'.format(ext, segment_module) function_name = segment['name'] contents_func = getattr(importlib.import_module(function_module), function_name) elif segment_type == 'string': From 00f749c9590b671fb7c0a7594674b100a37f6dc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Thu, 13 Dec 2012 13:10:10 +0100 Subject: [PATCH 29/31] Allow overriding segment contents when rendering --- powerline/renderer.py | 4 ++-- powerline/theme.py | 22 +++++++++++++--------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/powerline/renderer.py b/powerline/renderer.py index ff31e8c2..b60da225 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -12,7 +12,7 @@ class Renderer(object): self.segments = [] self.theme = theme - def render(self, mode, width=None): + def render(self, mode, width=None, contents_override=None): '''Render all segments. When a width is provided, low-priority segments are dropped one at @@ -21,7 +21,7 @@ class Renderer(object): provided they will fill the remaining space until the desired width is reached. ''' - self.segments = self.theme.get_segments(mode) + self.segments = self.theme.get_segments(mode, contents_override) rendered_highlighted = self._render_segments(mode) if not width: diff --git a/powerline/theme.py b/powerline/theme.py index 2c69c6ff..6a2c6bec 100644 --- a/powerline/theme.py +++ b/powerline/theme.py @@ -31,6 +31,7 @@ class Theme(object): highlighting_group = segment.get('highlight', segment.get('name')) self.segments.append({ + 'key': None if segment_type != 'function' else '{0}.{1}'.format(segment_module, function_name), 'type': segment_type, 'highlight': self.colorscheme.get_group_highlighting(highlighting_group), 'before': segment.get('before', ''), @@ -52,36 +53,39 @@ class Theme(object): ''' return self.dividers[side][type] - def get_segments(self, mode): + def get_segments(self, mode, contents_override=None): '''Return all segments. Function segments are called, and all segments get their before/after and ljust/rjust properties applied. ''' + contents_override = contents_override or {} return_segments = [] for segment in self.segments: if mode in segment['exclude_modes'] or (segment['include_modes'] and segment not in segment['include_modes']): continue if segment['type'] == 'function': - contents_func_ret = segment['contents_func'](**segment['args']) + contents = contents_override.get(segment['key'], segment['contents_func'](**segment['args'])) - if contents_func_ret is None: + if contents is None: continue try: - segment['highlight'] = self.colorscheme.get_group_highlighting(contents_func_ret['highlight']) - segment['contents'] = contents_func_ret['contents'] + segment['highlight'] = self.colorscheme.get_group_highlighting(contents['highlight']) + segment['contents'] = contents['contents'] except TypeError: - segment['contents'] = contents_func_ret + segment['contents'] = contents elif segment['type'] == 'filler' or (segment['type'] == 'string' and segment['contents'] is not None): pass else: continue - segment['contents'] = unicode(segment['before'] + unicode(segment['contents']) + segment['after'])\ - .ljust(segment['ljust'])\ - .rjust(segment['rjust']) + if not segment['key'] in contents_override: + # Only apply before/after/just to non-overridden segments + segment['contents'] = unicode(segment['before'] + unicode(segment['contents']) + segment['after'])\ + .ljust(segment['ljust'])\ + .rjust(segment['rjust']) return_segments.append(segment) From dd4c90fc680a8e9b1d3d4075a5ca8c8b683fe253 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Thu, 13 Dec 2012 13:16:09 +0100 Subject: [PATCH 30/31] Cache and retrieve segment contents for non-current vim windows This resolves the issue with non-current windows using the contents of the currently selected window. --- examples/vim/powerline.vim | 2 +- powerline/ext/vim/renderer.py | 22 ++++++++++++++++++---- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/examples/vim/powerline.vim b/examples/vim/powerline.vim index 33bba76a..093636e1 100644 --- a/examples/vim/powerline.vim +++ b/examples/vim/powerline.vim @@ -22,7 +22,7 @@ endfunction function! s:WinDoPowerline() if ! exists('w:powerline') - let w:powerline = '' + let w:powerline = {} endif let &l:stl = '%!Powerline('. winnr() .')' diff --git a/powerline/ext/vim/renderer.py b/powerline/ext/vim/renderer.py index 59dcadef..19ee4d59 100644 --- a/powerline/ext/vim/renderer.py +++ b/powerline/ext/vim/renderer.py @@ -21,14 +21,28 @@ class VimRenderer(Renderer): self.hl_groups = {} def render(self, winnr): + '''Render all segments. + + This method handles replacing of the percent placeholder for vim + statuslines, and it caches segment contents which are retrieved and + used in non-current windows. + ''' current = vim_getwinvar(winnr, 'current') winwidth = vim_winwidth(winnr) - mode = vim_mode() - if not current: - mode = 'nc' + if current or not vim_getwinvar(winnr, 'powerline'): + contents_cached = {segment['key']: segment['contents'] for segment in self.segments if segment['type'] == 'function'} + vim_setwinvar(winnr, 'powerline', contents_cached) - statusline = super(VimRenderer, self).render(mode, winwidth) + if current: + mode = vim_mode() + contents_override = None + else: + mode = 'nc' + contents_cached = vim_getwinvar(winnr, 'powerline') + contents_override = {k: contents_cached[k].decode('utf-8') for k in contents_cached.keys()} + + statusline = super(VimRenderer, self).render(mode, winwidth, contents_override) statusline = statusline.replace(self.PERCENT_PLACEHOLDER, '%%') return statusline From 09c2070bce123f2415a907a0f277b00ddb261c6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Thu, 13 Dec 2012 13:17:05 +0100 Subject: [PATCH 31/31] Remove unnecessary default values from vim theme --- powerline/themes/vim/default.json | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/powerline/themes/vim/default.json b/powerline/themes/vim/default.json index 65fb96db..0b2d1ac2 100644 --- a/powerline/themes/vim/default.json +++ b/powerline/themes/vim/default.json @@ -3,42 +3,35 @@ "segments": { "left": [ { - "type": "function", "name": "mode", "exclude_modes": ["nc"] }, { - "type": "function", "name": "paste_indicator", "exclude_modes": ["nc"], "priority": 10 }, { - "type": "function", "name": "branch", "exclude_modes": ["nc"], "priority": 60, "before": "⭠ " }, { - "type": "function", "name": "readonly_indicator", "exclude_modes": ["nc"], "draw_divider": false }, { - "type": "function", "name": "file_directory", "priority": 40, "draw_divider": false }, { - "type": "function", "name": "file_name", "draw_divider": false }, { - "type": "function", "name": "modified_indicator", "args": { "text": "+" }, "exclude_modes": ["nc"], @@ -51,26 +44,22 @@ ], "right": [ { - "type": "function", "name": "file_format", "draw_divider": false, "exclude_modes": ["nc"], "priority": 50 }, { - "type": "function", "name": "file_encoding", "exclude_modes": ["nc"], "priority": 50 }, { - "type": "function", "name": "file_type", "exclude_modes": ["nc"], "priority": 50 }, { - "type": "function", "name": "line_percent", "args": { "gradient": true }, "priority": 30, @@ -83,13 +72,11 @@ "highlight": ["line_current_symbol", "line_current"] }, { - "type": "function", "name": "line_current", "draw_divider": false, "rjust": 3 }, { - "type": "function", "name": "col_current", "draw_divider": false, "priority": 30,